3

I am thinking how to develop an application in a DDD way, and now I am thinking about the concurrency part.

In some examples I have seen that in the domain classes are injected with dependency injection an implementation of the repository, and also in the root aggregate is added a field version, to control the concurrency, so anytime a root method is called, it increases the version.

I don't like this solution so much, first because I don't know if the domain classes know if the data persists or not, so I don't want to inject a repository.

Second, the domain class is responsible for handling the concurrency, I think it is not the best option. For me, the domain classes have to do only the logic of the domain.

So in my case, is in my application layer where I inject the repository, and I work in this way:

class MyApplicationService
{
    MyApplicationService(IRepository paramOrderRepository)
    {
        _orderRepository = paramOrderRepository;
    }

    public void AddItemToOrder(Item paramItem, decimal paramAmount)
    {
       order myOrder = _çorderRrepository.GetOrder(1);

       myOrder.AddItem(paramItem, paramAmount);

       _orderRepository.Commit();
    }
}

The application gets the person to update using the repository, I call the method of the domain class and finally the changes are committed.

But thinking in the concurrency. The concurrency is handled in the root aggregate level, so I have in the Order table in the database a field for the version.

The problem is that here, really I am not modifying the order, I am adding a new item, so no fields in the order is modified, so for the database, there is no concurrency problem.

So I was thinking that perhaps, in the IOrderRepository I could have a method to increase version of an entity, so I could do this:

public void AddItemToOrder(Item paramItem, decimal paramAmount)
        {
           order myOrder = _çorderRrepository.GetOrder(1);
    
           myOrder.AddItem(paramItem, paramAmount);

           _orderRepository.IncreaseVersion(myOrder);
    
           _orderRepository.Commit();
        }

But I don't know if it is a good way to handle the concurrency or not. Although I am not very sure how to implement the IncreaseVersion() method of the repository, but but the moment I would focus in this aspect, I am thinking more in the general design, I would think in the implementation later. My intention it is to expose this possible solution.

Sure it would be easier if in domain class I would have a field for the version, so I could do that:

public void AddItemToOrder(Item paramItem, decimal paramAmount)
        {
           order myOrder = _çorderRrepository.GetOrder(1);
    
           myOrder.AddItem(paramItem, paramAmount);

           myOrder.IncreaseVersion();
    
           _orderRepository.Commit();
        }

But as I commented, I don't think this is a good idea to have a version property in the domain class, because strictly talking, in the obiquitous language, the experts don't tell anything something like "after add a new item to the order, it is needed to increase the version of the order".

So I don't know if I am wrong thinking that it is not appropriate to have a version property in the domain class, and I don't know if my solution is the best or which options there are to control the concurrency from a DDD point of view. So I would thank any suggestion or alternatives for that.

2
  • 2
    What would you want to happen for a completely non-computerised paper-based system? For example, if two different humans, each holding a physical paper copy of the same order sheet, both simultaneously decided they wanted to amend their own copies and submit them back to your domain experts; how would they resolve the differences between these two contradictory sheets of paper? Feb 12, 2023 at 0:18
  • 2
    If you expect a lot of these concurrency issues or, as this question suggests, you have strong objections against versioning your aggregates, you should consider pessimistic locking. However, your arguments against versioning in the domain model are a bit dogmatic imo. “The domain experts don’t talk about versions, so we can’t use it”. If you tried explaining the problem they might have a different solution or perhaps the use of version is fine. You can even hide the versioning related code in an AggregateRoot base class, keeping your actual aggregates clean while allowing optimistic locking.
    – Rik D
    Feb 12, 2023 at 11:24

5 Answers 5

2

There is nothing inherently wrong in adding purely technical attributes to entities (or other domain objects) which are exclusive required for implementing things like persistence or concurrent access. Depending on

  • your organizational environment
  • your tooling
  • the way you use to talk with your domain experts about the model

you either don't show those attributes to your domain experts, or you mark them somehow as "technical" and tell your experts to ignore them. For example, years before the term DDD was inventend, we used a graphical CASE tool to create a "domain model" (ok, it was more a database model), and the graphical representation of the classes was used in discussions with the experts. To implement the model, we used code generators which added a few technical attributes like uniform IDs and - surprise - version numbers for concurrent access. That worked pretty well.

However, it is not always necessary to have such a strict separation and tool support. In lots of organizations, it will be sufficient just to talk to each other. Most reasonable people will understand there are some technical necessities to add some attributes to a model they can safely ignore.

3
  • Thanks. I understand the point, that sometimes it is needed to accept that to don't make so difficult some things. Also I have read sometimes that the domain shouldn't know anything about implementation or technical aspects, and concurrency is something that I think it can be considerate in this way, so it is another reason why at first I try to avoid to make the domain to be conscious about the concurrency. Feb 14, 2023 at 8:29
  • @ÁlvaroGarcía: at the end of the day, domain models need to be implemented somehow, so statements like "domain shouldn't know anything about implementation or technical aspects" are clearly an oversimplification. Removing most technical aspects out of the domain model is fine, but certain things like identity and versions or history are hard to remove completely and rarely worth the hassle.
    – Doc Brown
    Feb 14, 2023 at 11:46
  • Yes, you are right, and thinking more about it, I think that from a point of view, is the domain who has to decide if an action needs to ensure the coherence or not. Later the persistance has to ensore that thechnically it is done correctly. Perhaps I could think that instead of the domain, I could increment the version in the application layer, but thinking in the application layer as layer the coordinate domain and persistance, perhaps to decide if increment or not it is to know about domain matters. So according to that, it seems domain is not a bad place to care about concurrency. Feb 14, 2023 at 16:22
2

One possible way to keep your domain model "pure" from persistence-related implementation details is not to persist your domain classes directly. Instead, you define a persistence model, and map between both models accordingly. Then, instead of defining repositories for your domain classes, you would define them for the persistent classes instead. For example, say you have the following:

domain.model.package

class Account {

  // various Account related state/behavior
}

You would then define the following:

persistence.model.package

class OptimisticallyLockedAccount {

  // All the state that domain.model.package.Account needs persisted.
  private Long version; // optimistic locking version field
}

class AccountRepository {
  // various query methods
}

class PersistenceToDomainMapper {

 domain.model.package.Account toDomainEntity(persistence.model.package.OptimisticallyLockedAccount account) {
  // instantiate account from persisted state
 }

 persistence.model.package.OptimisticallyLockedAccount toPersistentEntity(domain.model.package.Account account) {
  // instantiate persistent account from domain fields.
  // Note that you won't have information about the "version" field here,
  // so you will have to fetch that elsewhere,
  // for example a thread-local in-memory cache in the persistence layer every time you load a persistent entity from a repository.
  }
}

Thus your domain classes would be unaware of the persistence-related implementation details. However, it introduces additional complexity when you have to map from your domain entity to your persistence entity. You would need to keep the additional state included in the persistence entity elsewhere, and then update the fields that changed during the business operation.

1
  • The road to hell is paved with good intentions ;-) Using a separate persistence model is IMHO only worth it when the domain model and persistence model are structurally different, for example, for legacy reasons.
    – Doc Brown
    Feb 14, 2023 at 16:23
0

A version property in the domain class represents the model of order when it is referred in the discussion. Would be too elaborated to say "add an item to the most recent version of the order", so version is implicit, although isn't mentioned it is referred by business scenarios. This mention is just to set aside whether version is a detail that should exist in a model class.

The optimistic locking for persistence using relational databases is safest handled at the database level, that is in the SQL statement executed on the database. For this case this will translate to a conditional update of order's version with a WHERE clause testing the result returned by a conditional insert, an insert with a subquery having the optimistic lock test in WHERE clause.

-1

Domain classes with repository dependencies are implementation of active record pattern, objects encapsulating data and behaviour.

Although in multi user applications concurrency is a common concern, for the concept of ordering is unusual to consider concurrency. Concurrent order is equivalent to "multiple users placing one order" while most of the times is "one user placing multiple orders".

I've read that is wise to avoid the trap of finding a problem for a design pattern one wants to implement, just to close with a joke.

1
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Mar 1, 2023 at 4:11
-1

Optimistic lock is a technique that implies the database operations UPDATE and DELETE run successfully without a previous locking of record(s) being processed. The commit operation from the code snippet is part of a transaction that is the implementation of pessimistic lock technique avoided with optimistic lock technique by testing at query runtime the value of a version column or the values of all columns of the modified record(s) optimistic locking being a repository tier concern implemented either by data access objects (DAOs) or by object-relational mapping (ORM) frameworks when one is used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.