I hope everyone is having a great week and that you are ready for the weekend. I this post I would like to explore a interesting programming concept and technique called Domain Driven Design and/or Domain-Centric programming.
Let me go in more detail into this concept. Data-centric generally means that we build a system around an understanding of the data you’ll be interacting with. The typical approach is to first model a database. The second step is to then take the model (the database) and make sure that resembles the domain (the business). The third step is to take a multi-tier approach to the model and the business where the UI, business Logic and database live in different layers.
At this point you might be thinking, well, Microsoft Dynamics AX is built like this, and the answer is yes. AX, in my opinion, uses a domain driven approach as it has been build for a large number of business models. In turn, the actual business domain (a business that just purchased AX) can be coupled to AX and vice-versa.
Domain-centric design focuses on the problem domain as a whole, which not only includes the data, but also the behavior. So we not only focus on the fact that an employee entity has a First Name, but also on the fact that the entity can get a vacation, or a raise.
The tool used is object oriented programming (OOP), which relies on the power of classes and encapsulation. The idea behind domain driven design is to build a system in a manner that’s reflective of the actual problem domain we are trying to solve. This is where business users come into play. For example, they’ll help you understand how the actual system currently works (even if it’s a manual paper process. and/or existing AX process) and how it ought to work. .
Anyone who’s gone through the above knows that learning a new business is the most complicated part of any programming job. For that reason, there are real benefits to making our code resemble, as much as possible, the domain.
Essentially what I’m talking about is communication. If your users are talking about Strategic Outcomes, which a month ago meant nothing to us, and our code talks about Strategic Outcomes then some of the ambiguity and much of the potential misinterpretation is cleaned up.
Ultimately, this is the true purpose of an enterprise developer – to understand the problem domain.
Doing domain driven design (DDD) design doesn’t necessarily mean we have to start with modeling the domain but rather it means that we should focus on the domain and let the business (domain in this case) drive our next steps. We may agree at this point that we should start with our data model (or existing data model such a AX).
DDD extends our organizational toolbox and borrows from well-known industry patterns. For example, In AX modules help us organize a larger single model into smaller chunks.
In most enterprise systems there are course-grained areas of responsibility. DDD calls this top level of organization a Bounded Context.
Let's take workers' compensation insurance policies as an example. These need to be concerned with elements such as:
- Quoting and sales
- General policy workflow (renewals, terminations)
- Auditing payroll estimation
- Quarterly self-estimates
- Setting and managing rates
- Issuing commissions to agencies and brokers
- Billing customers
- General accounting
- Determining acceptable exposures (underwriting)
We could incorporate all of this into a single system, but in doing so leads us to many issues down the road. Business users might understand general workflow versus a policy in the context of payroll auditing in two very different ways. If we use the same policy class, we are pushing the limitations of that class and getting away from best practices such as the Single Responsibility Principle (SRP).
Systems that fail to isolate models often fall into an architectural style called The Big Ball of Mud. Also, DDD nudges you toward identifying contexts and constraining your modeling effort within particular contexts. We can use a simple diagram, called a context map, to explore the boundaries of our system.
Source: Microsoft
This is why when writing the code for our model, we need to understand the domain and resemble it (a representation of the model that is) in our code. For example, let’s assume for a minute that we are doing an implementation for a car dealership and that we've talked to our client and a few salespeople, and we’ve realized that a major point is keeping track of the inter-dependency between upgrade options.
Then, in code (and remember this is conceptual and does not have to be taken to AX and/or .NET) we’ll create four classes/objects to support the theory of "-dependency between upgrade options":
public class Car
{
private Model _model;
private List<Upgrade> _upgrades;
public void Add(Upgrade upgrade){ //todo }
}
public class Model
{
private int _id;
private int _year;
private string _name;
public ReadOnlyCollection<Upgrade> GetAvailableUpgrades()
{
return null;
}
}
public class Upgrade
{
private int _id;
private string _name;
public ReadOnlyCollection<Upgrade> RequiredUpgrades
{
get { return null; }
}
}
public class Car
{
private Model _model;
private List<Upgrade> _upgrades;
public void Add(Upgrade upgrade)
{
_upgrades.Add(upgrade);
}
public ReadOnlyCollection<Upgrade> MissingUpgradeDependencies()
{
List<Upgrade> missingUpgrades = new List<Upgrade>();
foreach (Upgrade upgrade in _upgrades)
{
foreach (Upgrade dependentUpgrade in upgrade.RequiredUpgrades)
{
if (!_upgrades.Contains(dependentUpgrade)
&& !missingUpgrades.Contains(dependentUpgrade))
{
missingUpgrades.Add(dependentUpgrade);
}
}
}
return missingUpgrades.AsReadOnly();
}
}
As you can see in the above code, we first implemented the Add method. Then we implement a method that allows us to retrieve all the missing upgrades for a specific car. The code should reflect all concepts exposed by the model. Classes and methods should be named according to the names defined by the domain.
Furthermore, associations, compositions, aggregations and sometimes even inheritances should be extracted from the model. Sometimes, during implementation, we realise that some of the domain concepts discussed and added to the model don't actually fit well together and some changes are necessary. When it happens, we should discuss the problems and/or limitations with the domain experts and refactor the domain model in order to favour a more precise implementation, without ever distorting the business significance of the design.
Ultimately, the code
is the most important artefact of a software project and it needs to work
efficiently. Regardless of what many experts in the subject say, code
implementation will have some impact on the design. However, we need to be
careful and very selective about which aspects of the code can influence design
changes. As a rule, try as much as we can to never let technical frameworks
limitations influence our design, but as we know, every rule has exceptions.
Becoming proficient with object-oriented programming is not an easy task. I believe is within the reach of most people, but it takes dedication, book learning, and practice. It also helps if you adopt an attitude of craftsmanship and continual learning.
Well folks, I hope you found this article interesting, and that it has helped you in any way to remind you of the basics of programming.
Until the next time.