Tuesday, February 9, 2016

Functional Domain Modeling. Step 1. Domain algebra.

Sometimes, the elegant implementation is just a function. 
Not a method. Not a class. Not a framework. Just a function.
--John Carmac on Twitter

Dear reader, if you reading this post then you probably already have good justification why it worth to learn functional programming, so I don’t want to spent your time by enumerating here another dozen of arguments! Instead, my aim is to help you to adopt functional paradigm for “every day” programming. I mean for implementing user stories which most of us do on daily basis.  

Also, I don't want to overload you with complex domain logic, but at the same time I’ll try to provide full of value and complete example of application with low-level coding details, so you could grasp “big picture”  and see how the functional programming could be used for writing code which solves business problem.

Challenging with problem.
Our task would be to implement parking application, let’s call it “GarageGuru” with the following requirements:
  1. The planned garage should consist of individual parking lots. Each parking lot has unique location and could accommodate specified type(s) of vehicle. For ex: only Motorbike, or only Car or both types.  
  2. Every parking lot could be in two states: “free” and “taken”.
  3. When user ask for parking then the application should look for available “free” parking lot, change its status to “taken” and return lot location where vehicle will be parked.
  4. When user wants take away his vehicle then the application should find “taken” lot where the vehicle was parked and clean it by changing its status back to “free”.    
  5. Every vehicle is distinguished by an unique identifier (for ex. licence plate).
  6. The application should allow to find location of parked vehicle by its unique identifier.

Why Scala language?

The functional paradigm itself is not a trivial thing to learn, so it’s very useful to domesticate it step-by-step, gradually applying new concepts as you go on top of well known OOP style. Since, Scala is hybrid language and it allows to balance paradigm style in a range from totally OOP-like up to totally FP-like, I would recommend to begin from Scala, especially for those who came from Java world.

The Algebra of API design.
In object-oriented development we begin our API design with some interface which publishes  the ultimate contract of the model to its client then we fill in this interface with concrete implementation object and classes. In functional programming we starting from defining Algebra of API, in other words we need to discover a data type and a set of primitive functions for our domain, and then derive some useful combinators. Let’s begin from modeling states machine: “free lot”->“taken lot”. In functional word the basic building blocks are immutable data types and function, so our first primitive functions for modeling state transitions are:

def takeParkingLot( freeLot: FreeLot, vehicle: Vehicle ) : TakenLot

def cleanParkingLot( takenLot: TakenLot ) : FreeLot

The VehicleFreeLot and TakenLot are some abstract data types, (so far we don’t care about concrete implementations) together with functions they form Algebraic API or Domain Algebra. Why it’s called Algebra? To cut a long story short, in math we call the following expressions algebraic:
2x + x = 3x;

by analogy we might also call algebraic the expression like:
VehicleFreeLotTakenLot

or in more domain friendly way:
Vehicle take FreeLot = TakenLot

For those of my readers who looking for more details about the functional way of handling state transitions, I would recommend to watch the following Mario Fusco's talk:



In addition to already defined functions, we also need the one which allows us to find free lot in a garage (repository):

def findFreeLot(vehicle: Vehicle) : FreeLot

But what if free parking is not found or some error happens while querying an underlying implementation of repository (for ex. database). Should we return null or throw an exception? Actually, by doing so we would violate fundamental principle of FP called “referential transparency” in other words our functions aren’t “pure” or “side effect free”. From FP point of view, the consequences of such violation are fatal - the function is no longer composable. Of course, there are tons alternatives, for example:

def findFreeLot(vehicle: Vehicle) : Try[FreeLot]

where Try[FreeLotis a container type which carries FreeLot or Throwable.  


Let’s add one more primitive function for searching parked vehicle and pack all of our Domain Algebra in a Trait. In scala the Trait is some sort of interface “on steroids”, it may contains variables, partially implemented methods, and lot of other useful stuff:  

trait ParkingService[FreeLot, TakenLot, Vehicle, VehicleId] {

  def findFreeLot(vehicle: Vehicle) : Try[FreeLot]

  def findParkedVehicle(vehicleId: VehicleId) : Try[TakenLot]

  def takeParkingLot(freeLot: FreeLot, vehicle: Vehicle): Try[TakenLot]

  def cleanParkingLot(takenLot: TakenLot): Try[FreeLot]

}

In the defined trait the part: [FreeLot, TakenLot, Vehicle, VehicleId] is definition of parametric types, which is equivalent of Generics in Java.


Composing functions into larger workflows.
Now, let’s consider use case n. 3 from the requirements list above, here we need to perform two steps: search for free lot and then take it, if such lot was found. For implementing given workflow it may be useful to reuse already defined functions: findFreeLot and takeParkingLot, we just need to chain them together so output of findFreeLot become input for takeParkingLot. Actually, one of the most prominent idea of functional programming is chaining and composition simpler functions into larger ones. Thus, the solution for our use case is completely supported by Scala syntax and looks like:

def parkVehicle(vehicle: Vehicle) : Try[TakenLot] = {
    for{
        freeLot <- findFreeLot(vehicle)
        takenLot <- takeParkingLot(freeLot, vehicle)
    }yield (takenLot)
  }

We’ve used “for comprehensions” for Monads where Try[_] is a Modad and this means that it allows sequencing of functions. If you are new in functional programming and Monad is alien concept for you, then you could start from watching talk by Scott Wlaschin Railway oriented programming



Yet another awesome resource which tremendously helped me in learning FP is the book functional programming in scala.


The final Domain Algebra.
By analogy with parkVehicle(..), we also could compose functions for implementing takeAwayVehicle(..) workflow  (mentioned in use case 4 from requirements list), so now the complete Domain Algebra looks like:

trait ParkingService[FreeLot, TakenLot, Vehicle, VehicleId] {

  def findFreeLot(vehicle: Vehicle) : Try[FreeLot]

  def findParkedVehicle(vehicleId: VehicleId) : Try[TakenLot]

  def takeParkingLot(freeLot: FreeLot, vehicle: Vehicle): Try[TakenLot]

  def cleanParkingLot(takenLot: TakenLot): Try[FreeLot]

  def parkVehicle(vehicle: Vehicle) : Try[TakenLot] = {
    for{
        freeLot <- findFreeLot(vehicle)
        takenLot <- takeParkingLot(freeLot, vehicle)
    }yield (takenLot)
  }

  def takeAwayVehicle(vehicleId: VehicleId) : Try[FreeLot] = {
    for{
         takenLot <- findParkedVehicle(vehicleId)
         freeLot <- cleanParkingLot(takenLot)
    }yield (freeLot)
  }

}


This what beauty of functional design is about! We have composed larger workflows from simpler ones, we have no idea of what our types FreeLot, TakenLot, Vehicle will look like or how we will implement the behaviors takeParkingLot(..) or cleanParkingLot(..). But we already have complete implementation of  parkVehicle(..) and takeAwayVehicle(..) which are formed out of simpler functions.

The interpreter for the domain algebra.


The actual implementation of the defined API  (which in functional programming called interpreter) is located in my github: https://github.com/ddd-fun, repository "garage-guru-fun", branch "di-impl":
https://github.com/ddd-fun/garage-guru-fun/tree/di-impl
Please, feel free to pull or browse it for getting more details.  In addition to FP application, I also implemented fully OOP-based equivalent in Java "garage-guru", so you could benefit from comparing both solutions.



Thursday, December 24, 2015

Strategic Domain Driven Design. Microservices.

The essence of Strategic Design in DDD is to segregate domain model into well defined areas by business objectives called sub-domains. To some extend, it is Separation of Concerns principle at  enterprise level. Having functionally decomposed domain into separate sub-domains with its own distinct bounded contexts, makes software systems much simpler and more responsive to business changes.

Let's imagine typical situation: we are working on legacy project related to banking area within a code base with no clear boundaries between sub-domains, so everything is tightly coupled, architecture leaning toward "Big bull of mud" which is developed and deployed as monolith application (for ex. as one war-file on Tomcat server). However, project is commercially successful and there is a constant need to grow and scale it, so we are challenged by re-factoring and improving architecture.

Making sense of Bounded Context.

We decided to begin re-factoring from deeper analysis of company core concepts: "Accounting" and transfer "Limits". After series of analysis we came up with idea to separate Account and Limit concerns into distinct sub-domains, so each of them would have its own well defined responsibility within its own module:
  • Accounting sub-domain is responsible for credit/debit operations and keeping balance;   
  • Limit Management sub-domain provides flexible functionality, which allows end-users to define the limit policy for their accounts;
The obvious question that we must be asking to our self is how interconnected should modules be. After all, the entire model has to work as a whole and there needs to be some interconnections even across isolated modules. Let's have a zoomed look on our model and consider the following use case:
When user wants to debit some amount of money from his account, associated limits must be checked before. If the amount exceeds limit then initiated operation must be interrupted and user should be replied with an appropriate message. 
Since Accounting operations depend on Limits (we need to check limits before debit), it looks like "Limits" entity belongs to both sub-domains (please, see Figure 1). Now the challenging question is: How to properly isolate two modules in the case where they are logically coupled?


 Figure 1. The fragments of "Limit Management" and "Accounting" sub-domains. 

The answer lies within business itself! The interesting thing was discovered during discussing "Limit" notion with domain experts. From "Limit Management" point of view, the "Limit" is defined by set of complex rules and policies. For example some of them are:
  • daily debit limit entered by user should not be greater than weekly one, otherwise it makes no sense; 
  • the maximum amount of overdraft limit depends on type of contract, customer profile and  monthly incomes etc.;
  • authorization rules, for ex. call-center agent could change some limits on user behalf.

At the same time from "Accounting" domain point of view, the "Limit" is just a threshold value which we should not exceed when performing credit/debit operations.

It looks like the word "Limit" is overloaded! Obviously, it has a different semantic meaning depending on the context. The same semantic dissonance happens with notion "Account". Within "Accounting" context it is responsible primary for keeping balance and credit/debit operations, whereas within "Limit Management" context, the "Account" is not credit/debit anymore, but rather it is just an unique identifier (number) limit rules are associated with.

Therefore, when it comes to the concrete modeling we also struggle to puck in all of "Limit" aspects into one entity. Indeed, if concept is overloaded then one single entity will be over-complicated too. Wouldn't be much cleaner if we explicitly defined what the "Limit" does mean for every contexts and implemented two distinct models of "Limit" so every one represents proper vision of target context?

The principle of model segregation is very often used in Software world. Think about Thread! In Java context the Thread model has some major methods such as: start(), interrupt(), join() allowing client code to manage parallel execution, anyway it is very tiny in comparing with what Thread is in context of OS. Indeed, the Thread model is much richer here, it encompasses such things as: execution stack, scheduling, counters, registers etc., it also could be Green or Hyper, or even not thread at all but Process. Could you imagine the level of mess if on Java level we were forced to deal with all those "richness" related to OS level?

How decoupling of two domains is not a big problem anymore. Since for "Accounting" sub-domain the "Limit" is just a threshold value (and here we absolutely don't care about other aspects which are specific to "Limit Management"), we don't have to share one Limit entity between two sub-domains. But what we rely need is to map one concept into another (please, see Figure 2).



Figure 2.  The fragments of  Context Mappings.

Let's go deeper into code and have a zoomed look on debit use case. It is a typical "check then act" workflow winch goes across sub-domain boundaries:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.ebank.accounting.app;

public class AccountingApplication {
  

  @Transactional
  public void debitAccount(AccNumber accNumber, Amount amount){

   Limits limits = this.limitService.getLimitsFor(accNumber);

   Statistics statistics = this.transactionsStatisticService.calculateFor(accNumber);

   Account account = this.accountRepository.findBy(accNumber);

   account.debit(amount, limits, statistics);

   this.accountRepository.save(account);
 }
  ...
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.ebank.accounting.domain.model;


public class Account {

    public void debit(Amount amount, Limits limits, Statistics statistics){

       if(limits.isDebitAllowed(amount, statistics)){
           doDebit(amount);
       }else{
          throw new ExccededDebitLimitException(amount, limits);
       }

    }
  ...
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.ebank.accounting.infrastructure.limits;


public class InternalLimitServiceImpl implements LimitService{
  
  private LimitApplication limitApplication;  

  ...  

  public Limits getLimitsFor(AccNumber accNumber){
      
   LimitsData limitsData = limitApplication.getLimitDataFor(accNumber); 
   
   Limits limits = translate(limitsData);    
   
   return limits;
  }
  ...
}

As you probably may mentioned, the InternalLimitServiceImpl is some kind of adapter, which communicates to "Limit Management" domain and maps foreign concept (LimitsData) into local one (Limits).

Thus, we made a sense of our Bounded Contexts and mapping between them. In his book "Implementing Domain Driven Design", Vaughn Vernon placed Bounded Context into chapter 2, immediately after introduction. Eric Evens said that he would also do that, if he had a chance to rewrite his own book "Domain Driven Design".  Both authors agreed  - Bounded Context is an corner stone of Strategic Design!

A Bounded Context is an explicit boundary within which a domain model exists.... It is often the case that in two explicitly different models, objects with the same or similar names have different meanings. When an explicit boundary is placed around each of the two models individually, the meaning of each concept in each Context is certain...

Some projects fall into the trap of attempting to create an all-inclusive model, one where the goal is to get the entire organization to agree on concepts with names that have only one global meaning. Approaching a modeling effort in this way is a pitfall...

The best position to take is to embrace the fact that differences always exist and apply Bounded Context to separately delineate each domain model where differences are explicit and well understood.
--Vaughn Vernon,"Implementing Domain Driven Design". Chapter 2.

Now it's time to scale at strategic level!

Very often along with separation of concerns, different non-functional requirements could be posed for sub-domains, for instance:
  • Business people might have different level of interest to particular sub-domains, some ones they want evolve and develop, whereas another ones are less susceptible to frequent changes. Usually, "hot areas" are the ones where competitive advantage comes from, and stable ones just supporting first ones. In DDD the first type called Core Domain, the second one - Supporting Domain.
  • Some sub-domains are under heavier load that other ones, so different scaling approaches is needed.
  • Some complex domains require long learning curve before newcomers became productive, so management prefer to avoid shuffling developers between domains. Team per sub-domain is more cost effective.
Taking on account the point above, we have discovered similar requirements for our banking application:
  1. From business point of view "Accounting" sub-domain is pretty stable so once we have implemented credit and debit operations, there are no reasons for frequent changes, whereas with limit concept it's absolutely opposite situation. The limit management is competitive advantage for our company, so business people expect great flexibility there. They are going to concentrate mostly on this area and deliver better and smarter functionality for our users. 
  2. Performance and throughput requirements are different too. On weekends, especially  during shopping time, we expect sharp peak of transaction volumes, thus "Accounting" sub-domain will be under very heavy load, whereas "Limit Management" is always under constant moderate load. 
  3. We want to have separate dedicated teams for "Accounting" and "Limit Management", so the first team will concentrate on deep technical stuff related to performance tuning and optimizations, whereas the second team will be more involved into collaborating with business people for improving functionality.
It looks like, we need shorter development iterations and more frequent releases for "Limit Management" sub-domain. On the other hand, in order to address scalability requirements posed to "Accounting", it would be great if we deployed it into some sort of auto-scaled environment. Therefore, we need to split our monolith application into two smaller parts, where every part would have its own distinct releases frequency and different deployment options.

Since we already have segregated sub-domains and established certain context boundaries, splitting monolith shouldn't be a big deal. We need only to re-implement LimitService interface, so in stead of internal in-memory call, it will make REST call to separately deployed "Limit Management" service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.ebank.accounting.infrastructure.limits;


public class ExternalLimitServiceImpl implements LimitService{
  
  private LimitRestClient limitRestClient;  

  ...  

  public DebitLimits getLimitDataFor(AccNumber accNumber){
      
   Response limitsDataResponse = limitRestClient
   .call(new LimitRequest(accNumber)); 
   
   Limits lmits = parseLimits(limitsDataResponse);    
   
   return limits;
  }
  ...
}

What we have done here? it is an elementary step toward Microservice architecture:

...the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery.
--Martin Fawler, http://martinfowler.com/articles/microservices.html

Autonomy is a crucial ingredient!

Having separated our monolith application into two smaller application (services) and allocated distinct team on each of them, we achieved a great success in solving point 1 and 3 from the requirements list above. Really, now every team could concentrate on their specific issues, without being forced to follow the same agile iteration, release procedures and interfering each others.  But the REST call between "Accounting" and "Limit Management" services (which is needed for checking limit during credit/debit operations) becomes a serious disadvantage:
  • performance and scalability suffers; one extra remote call takes more time to handle credit/debit request as well as puts extra load on "Limit Management" side.
  • reliability significantly decreased; if for some reasons "Limit Management" service is not available then wast majority of "Accounting" functionalities are blocked too;
  • harder to test; in order to perform "black box" functional testing of "Accounting" service  we need to set up some sort of mocked service which will imitate "Limit Management" functionality;
How could we solve this issue?  "Accounting" service needs to pull "Limit Management" service and this is strict dependency! Actually there is one life hack for this sort of issue: "invert pull to push!". Wouldn't it better if instead of pulling "Limit Management" for any single credit/debit request, the "Limit Management" service sends to "Accounting" some sort of notification every time when limits have changed on its side. In turn,  the "Accounting" service consumes this notification and update its own "local copy" of limit. So every time when checking limit is needed the "Accounting" service read it from its local database instead of calling remote "Limit Management" service. Actually, in DDD world the mentioned notification is called Domain Event which is published by "Limit Management" and consumed by "Accounting" sub-domains.

A greater degree of autonomy can be achieved when dependent state is already in place in our local system. Some may think of this as a cache of whole dependent objects, but that’s not usually the case when using DDD. Instead we create local domain objects translated from the foreign model, maintaining only the minimal amount of state needed by the local model.
--Vaughn Vernon,"Implementing Domain Driven Design". Chapter 3. Context Maps.

Thus, the  "Accounting" domain will have it's own "small" limit model with root entity - "Limits". It stores local copy of limits and it is updated every time when LimitWasChangedEvent is consumed by "Accounting" service: 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.ebank.accounting.infrastructure.events

public class DomainEventListener{
    
 private LimitApplication application;
      
  public void handle(LimitWasChangedEvent event){   
     
    application.updateLimit(readAccNumber(event), readLimit(event));

  } 
 ...
} 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.ebank.accounting.app.limits; 

import com.ebank.accounting.domain.model.limits.Limits;
import com.ebank.accounting.domain.model.limits.Limit;

public class LimitApplication{    
   ...
 
 @Transactional    
 public void updateLimit(AccNumber number, Limit limitRule){
       
   Limits limits = this.limitRepository.findBy(number);
      
   limits.addOrUpdate(limitRule);
   
   this.limitRepository.save(limits);
   
 }   
} 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.ebank.accounting.app.accounting; 

import com.ebank.accounting.domain.model.Account;
import com.ebank.accounting.domain.model.limits.Limits;

public class AccountingApplication {
  ...

  @Transactional
  public void debitAccount(AccNumber accNumber, Amount amount){

   Limits limits = this.limitRepository.findBy(accNumber);

   Statistics statistics = this.transactionsStatisticService.calculateFor(accNumber);

   Account account = this.accountRepository.findBy(accNumber);

   account.debit(amount, limits, statistics);

   this.accountRepository.save(account);
 }
  ...
}


Microservices is about distribution by business areas.

The Microservices seems to be promising architecture pattern, it might push scalability and resilience of enterprise to the next level as well as speed up development life cycles. But we always should take on consideration design issues. Since Microservices is about distribution by business responsibilities, the key to success lays on Strategic Design. In other words, on understanding of entire enterprise at higher level and ability to functionally decompose domain into Sub-domains and recognize Bounded Contexts where model is applicable.


Tactics without strategy is the noise before defeat. 
--Sun Tzu, "The Art of War" (around 500 B.C)

However, it worth to mention tactical design issues too. As we already seen,  remote calls are slower, much harder to test and are always at risk of failure. Therefore, the services should operate as autonomously as practical. Operations must execute largely independently of surrounding services, which means everyone has to manage eventual consistency. The proper eventual consistency could not be achieved without precise analysis of transaction boundaries, this issue is well known to DDD and addressed by tactical patterns: Aggregate and Domain Event. 


Tuesday, December 15, 2015

Enchance scalability with DDD. Part 2: Domain Event.

Properly identified consistency boundaries allowed us to spit one large aggregate in two smaller ones which in turns promise to scale better. Thus, two operations which modify "logically-independent" data chunks could be performed in parallel. However, very often when it comes to model real domain entities, they are not so "independent", but rather tightly coupled in such a way that modification (or creation/deleting) of one entity requires additional modification to execute on one or more other entities in order to respect domain consistency.

In previous post we have considered simple user management domain (it's highly recommended to read it before). By working with out domain experts Alice and Bob, we already discovered that software should handle simultaneous changing ParenUser's name and adding new ChildUser to the same ParentUser.

5. Multiple user could simultaneously change ParentUser's name and add new ChidlUser to the same ParentUser.

To make things slightly more complicated, let's add new rule:

 6. ParentUser should track number of its ChildUsers.

1
2
3
4
5
6
7
 public class ParentUser extends User{
  ...
   public int getNumberOfChildren(){
     //implementation
   }   
  ...
 } 

In other words, rule 6 means that number of ChildUsers in our system should be consistent with value returned by getNumberOfChildren(). It looks like we have encountered a new consistency rule which spans over ParentUser and ChildUser aggregates.

We already know that for sake of scalability we have to keep aggregates as small as possible and add new entities forming larger aggregates only when  true consistency rule is identified.

So it seams this time we could justify referencing our entities:   

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 public class ParentUser extends User{
   
   private Set<ChildUsers> childUsers;  
 
   public int getNumberOfChildren(){
     return children.size();
   }
   
   public void addChild(ChildUser newChildUser){  
     this.childUsers.add(newChildUser)  
   }   
 
  ...
 } 

In fact, this solution comes us back to violation of domain rule 5! But we don't want to disappoint Alice and Bob again!

What if we keep Disconnected Model solution, but add simple counter to ParentUser and increment it every time when new ChildUser is added to our system.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class ParentUser extends User{
   
   private int childrenCounter;
   ...

   public int getNumberOfChildren(){
     return this.childrenCounter
   } 

   public void incrementChildNumber(){
    this.childrenCounter++;
   } 
  ...
 } 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class UserApplication{
   ...

   @Transactional
   public void addNewChildUser(Name name, UserId parentId){
     ChildUser child = new ChildUser(name, parentId);
     this.repository.save(child);

     ParentUser parent = repository.find(parentId);
     parent.incrementChildNumber();
     this.repository.save(parent);

   } 
  ...
 } 

The problem of the solution above is - it violates another fundamental rule of modeling aggregate.

Modify only ONE aggregate instance per transaction!

A properly designed Bounded Context modifies only one Aggregate instance per transaction in all cases. What is more, we cannot correctly reason on Aggregate design without applying transactional analysis. 
Limiting modification to one Aggregate instance per transaction may sound overly strict. However, it is a rule of thumb and should be the goal in most cases
--Vaughn Vernon. Implementing Domain-Driven Design

Keeping in mind performance and scalability, our last solution is even worst than large aggregate one. Indeed, it still violates rule 5 and involves more entities into transaction. Actually, modifying multiple aggregates instances in one transaction dramatically limits scalability. For example, if we store each aggregate type in different relational database then we would need distributed transaction. If we store them as documents in NoSQL database (such as mongoDB), then we would get transaction issue again (most of NoSQL solution doesn't support modification of several documents in one transaction).  And even though we store them in the same oracle db, we still are not free of: "ORA:08177 cannot serialize access to this transaction",  because of simple rule: more data involved in transaction - higher chances of  concurrent conflicts.

Just because we are given a use case that requires for maintaining consistency in a single transaction doesn’t mean we should do that. Often, in such cases, the business goal can be achieved with eventual consistency between aggregates. 

Domain Event pattern.

There is a practical solution for achieving eventual consistency in a DDD model. An Aggregate publishes a Domain Event which is in some period of time delivered to one or multiple subscribers.

One point could be added to the tweet above: Language of Reactive systems: Events.  The Event is powerful tool and not only for achieving eventual consistency. Sometimes, for facilitating and accelerating analysis of target domain, DDD experts practice Event Storming technique, which leads to fully behavioral model, with no underlying data model. Focusing primary on behavior is crucial strategy for scalable solution (just remind how we came up with Disconnected Model in previous post), thus Event Storming might also help to shift mind for DDD beginners who use to focus mostly on data models.  

The key thing here is to properly understand what is Domain Event. A domain event captures an occurrence that happened in a domain and what poses interest for domain experts. The domain expert is not interested in databases, web sockets, or design patterns, but in the business domain of the things that have to happen. Domain events capture those facts in a way that doesn't specify a particular implementation. 


Let introduce new Domain Event into our possible implementation:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class UserApplication{
   ...

   @Transactional
   public void addNewChildUser(Name name, UserId parentId){
     ChildUser child = new ChildUser(name, parentId);
     this.repository.save(child);

     ChildUserWasCreatedEvent event = new ChildUserWasCreatedEvent(child);  
     this.eventPublisher.publish(event);

   } 
  ...
 } 

Please, pay attention modifying aggregate and publishing event should be transactional, so once aggregate changes are persisted and corresponding event is published, an underlying  messaging infrastructure should guarantee delivery to a consumer.  Event name is always in past tense, since it represent a fact that already happened in our domain.

There may be one or more consumers of our domain event, which consume it asynchronously. The task of consumer is to execute business rule making domain model eventually consistent. If event processing is failed, an appropriate re-delivery strategy must be provided.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class DomainEventListener{
   ...

   @Transactional
   public void handle(ChildUserWasCreatedEvent event){
     ParentUser parent = repository.find(event.getParentId());
     
     parent.incrementChildNumber();
     
     this.repository.save(parent);     

   } 
  ...
 } 

If we store aggregate events permanently in an event log, then we might notice that we could reconstruct aggregate state by applying all stored events on given aggregate in chronological order, thus entire aggregate state could be stored as its event log. This technique called Event Sourcing. Despite the obvious performance hit of fetching such aggregate from database, it gives enormous possibilities for business, especially for analytics and data mining. Along with CQRS this architecture style is well known and adopted in DDD community (for getting more info please see Greg Young talk).
 
The Event pattern is inevitable artifact of distributed high scalable systems, once we get the hang of using it we will be addicted and wonder  how we survive without it until now. It worth to mention that the biggest challenge here is to come up with appropriate architecture and to chose underground infrastructure. The range of issues and possible solution is too large for blog format, so for getting deeper insights, I would recommend to read Vaughn Vernon's Implementing Domain Driven Design, chapters 4 and 8.