Domain-driven design {model integrity}

Coding (Php 7.x)

Jul 11, 2020

How to take appropriate measure to ensure the success of a web project
 
/img/blog/domain-driven-design-model-integrity.jpg

Introduction

 

There are three phases on every web developer’s journey.

 

In the beginning, you do not really know what you are doing, 

 

you read some manual, watch some tutorial on youtube and start to build really simple applications.

 

Who has never done a todo list or blog management?

 

Once you get more experience and you decide that this is the path you’d like to follow more seriously you might decide to buy some course or spend some time reading about design pattern, principles, testing and why not Domain-driven design.

 

Eventually, you master the skill.

 

You are now able to create clean code with scalable and uncoupled features.

 

Most importantly and you can confidently choose where and when to apply these rules.

 

The Holy Grail of programming!

 

This is very common and is the same for individuals that write their own code and for groups of developer that collaborate of a software.

 

Throughout the series, we have seen how to create proper code.

 

In this episode, you are going to discover how to combine this effort when dealing with multiple coworkers.

 

 

 

 

Domain-Driven Design: The series

 

The article you are reading right now is part of a series regarding DDD.

 

In this series, we go through the main concepts of Eric Evans’s book and Abel Avram & Floyd Marinescu’s summary to make you able to better manage your task and create software that helps your client or that makes you manager happy.

 

  1. DDD the basics
  2. The building block of DDD
  3. How to preserve model integrity

 

 

Preserving Model Integrity

 

If you are working within one or more team you already know that there are a multitude of challenges to be able to deploy your code.

 

Often these teams rule under different managements, are coordinate in a different way and may also use different tech stack.

 

When designing these projects we should keep the domain model in mind.

 

Usually, a company starts with a single and straightforward model, while the business grows this model became inconsistent and full of contradiction.

 

The consistency present within a model is called unification. 

 

The perfect solution is reached by dividing the big model into smaller ones.

 

The reason is that several models, that are well integrated, can evolve and get bigger independently from one another.

 

To successfully create these models, Each of them should have a defined border and precise relationship between each other.

 

 

Bounded context

 

A model always has a context,

 

When we create an application with a single model the context is implicit

 

If you and your team are creating an application what needs to interact with other software you’ll need to define the context for each of the models.

 

Dividing a model into smaller one is not a correct science.

 

There isn’t a default blueprint that allows you to do that, you know that a context of a model is created by a set of rules that need to be applied.

 

Something you need to keep in mind the scope of the models you want to create.

 

It is very difficult to keep the model focused on one thing when its code spans over the entire project.

 

A way to make your life easier is to keep your model in charge of a specific area of your code.

 

delimit those boundaries in term of modes specification and team organization.

 

 

Continuous integration

 

Having specified boundaries and working in a bounded context is not an assurance of great code that will never encounter any error.

 

The elements of the various teams still have to communicate with each other and be sure that all of them understand the purpose of every item inside the model.

 

If someone does not understand the aim of the model or its relationship with other models he can modify the code in opposition of what the original meaning was supposed to be.

 

A model, like the development process behind, is not 100% defined from its beginning.

 

In fact,

 

It evolves and improves over time.

 

Some web developers can add a feature that someone else can later refactor and discover that some new improvements can be done and so on.

 

This is the reason why continuous integration has become a fundamental part of the web development process.

 

 

This process is in place to make sure that all the new elements added to the code fit the previous code, do not break anything and, if possible, improve its performance and quality.

 

As a rule of thumb remember that the earlier we merge the code the better

 

 

Context map

 

As discussed above enterprises applications should have multiple models,

 

In an ideal world, each of these modes should have their own bounded context.

 

Meanwhile, every developer belongs to a team of the business it is good to have an idea of the overall picture.

 

The context map is that document that shapes the different bounded context and their relationship.

 

The functionality of each model is just a little part of the entire system, that is why having separated models is not enough.

 

Each Bounded context needs to have a name that is part of the ubiquitous language.

 

This is because everyone that is part of the project should know what are the boundaries of each context.

 

The common way to do that is to define the contexts, then create the modules for each context.

 

Below you will see different patterns that can be used to create Context Map 

 

 

Shared kernel

 

When functional integration is limited, continuos integration may not be the right choice.

 

In fact, when teams do not have the skills or the organization necessary to maintain continuous integration it often results in more issues than good outcomes.

 

The problem is that if teams are not coordinated they can still work or related parts of the application but what will be produced won’t fit together nicely.

 

They could eventually spend more time on translation layers then they would if they had implemented Continuous Integration in the first place.

 

It is very easy to understand how this can result in losing the benefit of the common Ubiquitous Language.

 

If instead useful to plan to designate a shared subset of domains models to the two teams.

 

The goal of having this shared kernels is to reduce duplication and still maintaining two separated contexts.

 

All the teams can modify the code of the kernel and they have to integrate the changes.

 

If the teams use separate copies they have to merge the code as soon as possible.

 

Also, a test suite should be in place so every change done to the kernel can be tested straight away.

 

 

Customer-supplier

 

Sometimes two subsystems have a special relationship,

 

One depends on the other and the contexts in which those two subsystems exist are different.

 

Let’s take an example to better explain the customer-supplier relationship.

 

Let’s say we are working on an e-commerce website, and it has a reporting and messaging system too.

 

In this case, the two are completely different system so we should separate the context (no shared kernel here).

 

What we know is that the shopping part of the application does not depend on any case of the reporting part.

 

A customer can check for a product, add it to the cart, buy it and the system will work just fine without even considering the existence of the reporting app.

 

All the records are saved in the database, the only data in which the e-shopping is interested in is the number of products available in stock and the personal information of the customer.

 

On the other point, the reporting tool is very interested to know info such as abandoned chart, how many customers in a certain period of time, from where they come from etc.

 

It is important to notice that both apps will need to use the same database.

 

It is also important to understand that very likely the schema of the database will change over time,

 

Probably because of updates on the shopping part.

 

For this reason, these updates won’t be a problem for this part but it will be for the reporting one.

 

This pattern works very well when the team is under the same management, this makes the process of making decision way easier and creates harmony.

 

When we have such a scenario we should see the reporting team as a customer and the shopping team as the supplier.

 

When the shopping team need to make changes on the database or any other changes that will affect the report, they must communicate the updates to the counterpart.

 

 

Conformist

 

The Customer-supplier relationship that we have seen in the previous section is possible when both parts give their availability or work and show a willingness to be part of the relationship.

 

The problem is that, in real life, often the supplier has its own deadline and cannot be willing to communicate 100% of the time.

 

Communication is difficult and the supplier may not be interesting in spending too much time on it.

 

In case two development team have a customer-supplier relationship and the supplier does not care about providing for the customers’ need, the customer is helpless.

 

When this happens the customer does not have a lot of options, 

 

The main one would be to separate itself from the supplier and work completely on their own.

 

Other time they can choose to maintain the relationship with the supplier but take some measures to protect themselves.

 

They will want to implement a translation layer which connects the two contexts.

 

If the customer has to use the supplier ‘s model (and if it is well done), this can be time for conformity.

 

The customer team could adhere to the supplier team’s model.

 

In reality, this will look a lot like the shared kernel that we saw above but there will be some differences.

 

In here, the customer cannot make changes to the code. They can only use it as part of their model.

 

 

Anticorruption Layer

 

It may have already happened in your career as a web developer, if not get ready because it will soon, that you need to create an application that must interact with legacy software or separate applications.

 

Even if this legacy code is well done our model will probably be quite different.

 

For this reason that has to be a level of integration between our model and the legacy one.

 

There are few ways to interact with an external system, one can be by the network communication protocol, another via the database.

 

The external system uses data stored in a database and our new application needs to use the same data.

 

In this case, we are simply dealing with the transport of primitive among systems.

 

There are several risks on updating the client with our new application, data cannot work as it is supposed to be.

 

What we need in this case is to create a layer that will be able to adapt to the response we need.

 

This layer should stand between our client model and the external one.

 

The anti-corruption layer talks to the external model thanks to the use of the external language, not the client one.

 

 

The best way to see this layer is as a service, 

 

This service will be used as a facade, it needs to contain a translator that get the data and send it in the format we are interested in and the adapter.

 

A layer can have more that one service but each of them will have the same structure.

 

 

Separate ways

 

If a complex application is developed by more that one team, it may happen sometimes that one of these teams has to spend time implementing some requirements that are needed by the other team.

 

Develop your code independently is very different, (and much easier) than organize this code in such a way that it fits into another framework or system.

 

It may be necessary to alter the model only to make work with the subsystem related to it.

 

For these reasons, sometimes the teams may actually choose to go in Separate ways.

 

The Separate ways pattern is the best choice when the application of an enterprise can be build using different smaller applications that have nothing or very little in common between one another and from the model.

 

The way to implement this pattern is to look at the applications’ requirements and check if it is possible to divide it into two or more sets that do not have anything in common.

 

It that is possible then, it would be also possible to create separate bounded contexts and do independent modelling.

 

If you choose to opt for Separate Ways, you need to be sure that you won’t go back to an integrated system because models that are developed independently are very difficult to be integrated together.

 

 

Open host service

 

A good practice when two systems need to be integrated is to create a translation layer between them.

 

We use this layer as a buffer between an external system we want to integrate and the client.

 

In case the external subsystem needs to be used by more than one client we need to create translation layers for all of the subsystems connected to it.

 

The issue here is that as the number of external subsystems increases the cost of maintaining the application increase too.

 

A solution is to consider the external subsystem as providers of services.

 

If we can create a set of services around it, all the other subsystems will access these services and no translation layer would be needed.

 

You need to define a protocol that gives access to your subsystem by considering it as a set of services and only open this protocol as the one that needs it can use it.

 

 

Distillation

 

In the majority of the cases, a large domain has a large model, even after it has been refined.

 

Often it will remain larger even after many refactoring.

 

In a case like this, it is good to opt for a distillation.

 

The idea behind this is to define the Core Domain of the application and set apart everything that is not part of it.

 

The core domain for the system can change according to how we look at the whole application.

 

In fact, depending on the final goal the application aim to reach, the Core Domain can easily be misinterpreted.

 

It is of fundamental importance to correctly identify the core and its relationship with the other parts of the model.

 

In a large team, it is advisable to apply the best web developers to the Core Domain and spend more time and effort in the Core to find a deep model and develop a higher-level design.

 

The Core Domain is not created in a single step, it is a process that is refined with time and experience.

 

Refactoring happens more often than not, and the goal is to have code as clean as possible.

 

Once the team has the Core covered it would be time to work on the subdomains, that however, need to be considered as a lower priority.

 

There are a few ways to implement Generic Subdomain

 

  • Off-the-shelf Solutions Which means having the whole system already done by someone else
  • Outsourcing Where the design is given to a completely different team, this method lets the team focus 100% of the time on the Core Domain
  • Using an existing Model use an already created model
  • In-House implementation If no better solution is found externally than the most productive way would be to create internally,

 

 

become a Patron!

 

 

Conclusion

 

Being part of a large web development team is not an easy task.

 

You need to be sure that everything runs smoothly as well as still being productive and deploy new feature as soon as possible.

 

Proficiently doing this is not an easy task and there have been several senior developers and managers that have tried it.

 

One of the methods that shine more than anything else is the use of DDD by the concept of Eric Evans and his book.

 

In this small series, you have seen the basics and you should be now ready to implement it in your team successfully.

 

If you missed the beginning of the series and you want to start the series from the start head over the basics of DDD if instead, you are more interested in the actual code (and PHP in particular) you can learn some trick by brushing up the basics of PHP

 

 
 
If you like this content and you are hungry for some more join the Facebook's community in which we share info and news just like this one!

Other posts that might interest you

Coding (Php 7.x) May 27, 2020

Domain-driven design {building blocks}

See details
Coding (Php 7.x) Jul 17, 2020

Introduction to Composer [installation and components]

See details
Coding (Php 7.x) Jul 24, 2020

Introduction to Composer [Command-line interface]

See details
Get my free books' review to improve your skill now!
I don't want to improve