I have seen lots of CRM code and my initial thoughts are CRM developers don’t seem to create many classes or spend much time designing their code. In this blog post I will discuss the effects of not creating classes and explain the benefits of classes and abstractions.
Are CRM Developers afraid of creating classes?
The problem seems to be self reinforcing. Previous CRM projects contain few classes and most of the code exists in the plugin, this sets an unspoken standard which encourages developers to not create classes.
This is a mixture of a safety in numbers/wisdom of crowds approach, it could be seen as consistency and a standard approach to CRM development is good. Consistency in CRM development helps developers maintain, debug and extend CRM projects.
In the case of putting code in the plugin and not creating classes I think it’s bad practise and every plugin you add creates more technical debt for the CRM project and CRM projects quickly resemble legacy projects with complex CRM customizations
The end result is complex code doing lots of things and impossible to reuse and extremely difficult to work with (maintain, debug, read, etc).
To not use classes and put all or the bulk of the code in the plugin class
- makes it impossible to reuse code
- create complex code which does many things
- creates duplicate code
- create code which is hard to understand
- Code is very difficult to test
CRM plugins are often small, focused code but to not create classes or abstractions is to create a code base technical debt. This debt is paid back when CRM developers take longer to interact with the code in the future.
Plugins with few or no classes results in each developer creating new code every time because it’s impossible to reuse any of the code in the other plugins.
If CRM developers are not creating classes they are creating legacy code and plugins contain monster methods – Hosk
Boy Scout rule
The focus when creating code should be to keep the quality high, test everything and keep code as simple as possible.
When you create plugins or every time you make a code change you make you should leave the code in better state than you found it. Why you should write code and customizations like a boy scout
Quality of code is fundamental to the long term success of a CRM project and as soon as you lower your coding standards and let in bad code, it will soon spread Bad code is like a virus, don’t get infected
What is the role of Classes in programming
I advocate no code logic should be added to the plugin class except a call to a class passing in the entity, IOrganisationService, ITracing.
Putting logic in a plugin creates complex code, which is difficult to unit test but there are other reasons to create classes which are listed below
No Duplicate code
Duplicate code is one of the worst code smells and it has multiple bad side effects
- More code = more potential bugs
- duplicate code = bug fixing can result in multiple fixes
- bug fix one set = miss out changing duplicate code, which one is right?
When ever you find duplicate code or are about to create duplicate code, take the opportunity to find the common behaviour and abstract it into a class. This will enable you to reuse this single piece of code in many places.
Using classes and functions is the primary weapon against duplicate code because you are logically sorting code in logical class/method, which will allow developers to easily find and use it.
Modelling real world and abstract ideas
Classes allow you to model real world objects (account) and abstract concepts (printing, validating). The benefit is you can split up the code into logical abstractions which give clarity to the intent of the code.
CRM out of the box models plenty of real world objects
- Accounts
- Contacts
- Opportunities
- Phone Calls
Abstract classes
- Security role
- Solution
- Behaviours
You can create classes to model behaviours, this could be things like validating, printing, logging. These behaviours can be use in composition and used in multiple classes. The benefit of modelling behaviours is reuse
- Create abstract objects
- Create real world objects
Reduce complexity
Classes reduce complexity by splitting code into abstract or logical sections. The term abstract can be confusing, instead replace it with logical groups. Splitting code into logical groups organises the code into smaller/reusable sections.
Breaking the code into classes creates cohesive code and reducing dependencies between classes.
Creating well named classes with well named methods reduces complexity and makes the code using those methods and classes easier to read/understand.
Creating classes and methods allows you to create descriptive methods, e.g. call the a print method is easier to understand than reading all the lines of code needed to print. At a high level you might not need to know all the details of printing.
Manage the effect of change
Classes help reduce the effects of change by removing dependencies between classes (assuming the classes are well designed). Using abstract classes can remove the effects of change on the code using the abstract classes.
Well designed code can make it easy to add new types by encapsulating what varies and programming to interfaces. Adding a new type should not effect the other types or the code using the abstract class.
Managing the effects of change can reduce dependences and stop changes in code unexpectedly breaking other parts of the system. You could rename this to limiting the effects of change by reducing the ripple effect change has on badly designed code.
Information hiding
Read my blog post Good coding practices – Information hiding
hold data instead of lots of parameters
You can structure classes to hold the data instead of passing lots of variables between methods. This makes the code easier to understand and interact with.
Create reusable code
Many CRM projects I have worked on/inspected have had almost no code reuse partly due to the lack of classes created. Part of the reason is attitude, CRM developers don’t view creating reusable code as a priority.
CRM organised internal entities to make it easier to understand how it works. If we take security roles as an example.
The security role entity contains the main privileges which have a value of TRUE or FALSE and one of the four access levels Global, Business Child, Business unit, user.
Every time we add a new entity, we can set privileges for Create, Read, Write, Delete, Append, Append To, Assign, Share.
Whilst thinking about the example above I would except Privilege to be a separate class but we can see understanding the data in this structure is easier.
The benefits of designing classes
The clean coder has a good change on classes which talks about change isolating change
Needs will change, therefore code will change. We learned in OO 101 that there are concrete classes, which contain implementation details (code), and abstract classes, which
represent concepts only. A client class depending upon concrete details is at risk when those details change. We can introduce interfaces and abstract classes to help isolate the impact of those details.
Abstraction and modularity allows developers to work on separate parts of a system without having a detailed knowledge of the whole system.
Plugins allow developers to create new plugins without affecting other plugins. The crm dev needs to check there are no other plugins being triggered on the same entity. Good design of the code is based on on the plugin interface and not the concrete class e.g. your plugin.
Well designed classes have many benefits
- The code is created from many simple classes and methods
- The code is easy to unit test
- Well designed classes have minimal dependencies
- Well designed classes should be able to be reused
- Classes and method can make the code easier to understand
- Good abstractions can be used in different projects
Coding to an interface
The benefits of coding to an interface and using classes behind the interface is you limit the effects of change behind the interface.
You can add new types behind the interface without affecting any of the code which uses the interface.
A quick example is routing records to different users or teams, for this you could create an interface called Route and then concrete classes will contain the routing details and default value of the users/teams.
Route – Interface
- RouteToTeam
- RouteToDefaultTeam
- RouteToSalesTeam
- RouteToMarketingTeam
- RouteToUser
- RouteToSalesManager
- RouteToCaseManager
The benefits of coding to an interface and using classes
- Any code using the Route interface will be unaware of what specific type of route they are using.
- The code calling the interface has no knowledge of what objects, variables are used to route, it calls the Route interface methods
- Its easy to add new Routes which will involve changing minimal amount of code
- The code using the Interface is decoupled from the concrete classes
- The effects of change are restrained by the interface
- Easy to test
- Selecting the type can be done dynamically at runtime
Dependency Inversion Principle (DIP)
Reading about the Dependency Inversion Principle (DIP) helped me understand abstractions and the benefits of using them. To remind you what DIP is
Depend on abstractions, not on concretions.
Wiki adds two more points to help understand
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.
I highly recommend you read Uncle Bobs article The Dependency Inversion Principle, where he explains it a lot better than me.
This article DIP in the Wild has a good example of using DIP which helps understand the concept and it’s usage in developer life.
Abstract ideas can be tricky to understand, to make this easier to understand think of black boxes.
Black boxes are code which works but the user of the black box doesn’t know how the code works inside the black box. The internal code can change but the consumers of the black box wouldn’t know.
Change has been isolated inside the black box, Interfaces and abstract classes operate as black boxes hiding the change behind them. Any code which interacts with an interface/abstract class won’t need to change if the internal workings behind the interfaces/abstract classes change.
A good example is using the CRM SDK to interact with the CRM database. Microsoft can change how the internal workings of the CRM SDK change and can change the CRM database structure (in CRM 2015 they removed CRM database extension table).
Microsoft removed a whole table but CRM developers who used the SDK wouldn’t have noticed any difference (I didn’t notice) but those who developed code/reports directly against the CRM table would suddenly find their code/reports no longer worked when they upgraded to CRM 2015.
The next blog in the series is how to find Classes
Yes, I absolutely agree with you!.
It would be interesting to make a comparison of this with the generation of classes in JavaScript, with namespaces, functions, and so on.I think it is harder to maintain the same granularity and reusability in code that you can achieve with .Net classes.
LikeLike
Thanks for this articel. May I ask how you solve the “external” DLL probelm? Are you using ILMerge or LinkedTypes?
LikeLike
To reuse code inside a plugin you can create a shared code repository and add the code into your plugins project so all the code exists within the one dll.
We currently have a code repository which doesn’t exist in the plugin projects and then add them as linked files. If you were going to change those files and adapt them for the current project you would need to take a copy/branch.
Some of the functionality you can put into it’s own solution and use it (if you don’t need to use the code in your current project).
LikeLike
I have a Visual Studio project template that I created that contains all the base classes and helper code that I use along with stubbed out Plugin and Activity classes which inherit from a common base class that handles all the service creation, etc.
LikeLike
Great idea, particularly if there are many CRM developer working on a project, it will create consistent plugins and remind them to design their code.
LikeLike
Really helpful article, I hope more CRM developers follow suite. I think one of the barriers for this is the need to use ILMerge to merge the shared assemblies. I have just set this up for my next project and found Nicolas Lewinski’s article on using ILMerge very helpful: http://nicknow.net/dynamics-crm-ilmerge-dll-plugin/
For developers that are a little shy of messing with the build process the CRM Solution manager as mentioned in your article https://crmbusiness.wordpress.com/2015/06/10/crm-developer-toolkit-alternatives/ claims to do the ILMerge for you. I plan to look at this for another future project for CRM 2015.
Thanks for the great articles, if only I had more time to read and action all of them.
LikeLike
Thanks for the comment Phil.
I haven’t used ILMERGE myself but one of the common problems I hear about ILMERGE is it’s very difficult to debug and step through the code.
Most experienced CRM developers I have talked to, recommend not using ILMERGE and bring in the code into your CRM projects. To do this you would probably need to split the code into small focused areas and bring in the bits of code you need for each Plugin project or look to deploy some of the functionality as stand alone solutions.
LikeLiked by 1 person
Hi Ben,
ILMerge can be used safely in your projects. Actually it is not harder to step through your code as long as you put all pdb files in the assembly folder on the CRM development server.
However setting up a proper script for ILMerge can be a bit hard. Make sure you don’t include any dll containing the name “Microsoft”.
Bas
LikeLike
I posed a question about ILMerge and referencing 3rd party assemblies last year on Microsoft Dynamics community, Scott Durow fed into this and was an interesting time. https://community.dynamics.com/crm/f/117/t/200925 also if you looking to setup ILMerge or explore options wrote a 2-part series https://antonyellisdynamicscrm.wordpress.com/2016/10/12/how-to-register-an-early-bound-library-within-microsoft-dynamics-crm-online-sandbox-isolation-mode-part-1/
LikeLike
I would say that the primary barrier I see when taking a layered class system aproach within a plugin is the whole pre-image / post – image / target thing. I always have doubts on how to build classes that handles the correct entity object. On the other hand, in Workflow activity objects, is a no brainer.
LikeLike