Efficient unit design. Single unit simulation

This article is a synopsis of Effective Aggregate Design Part I: Modeling a Single Aggregate .





Combining entities and value objects into an aggregate with carefully thought out consistency boundaries may seem simple, but of all tactical DDD patterns, aggregate is one of the most complex.





It will be helpful to start with some general questions. Is an aggregate just a way to combine closely related objects with a common root (Aggregate Root)? If so, is there some kind of limitation on the number of objects that can be in the graph? Since one aggregate can refer to another, is it possible to navigate through the aggregates using these links and change the data of the objects included in a particular aggregate? And what is the invariant and the consistency limit ? The answer to the last question largely influences the rest of the answers.





There are many ways to model an assembly incorrectly. We can design an oversized unit. On the other hand, we can divide all aggregates in such a way that as a result the true invariants are violated. As we will see, it is imperative to avoid such extremes and pay attention to business rules instead.





ProjectOvation Application Development

Let's take a look at aggregates with an example. Our fictitious company is developing an application to support projects based on the Scrum methodology. The application follows the traditional Scrum project management model, that is, there are a product (product), a product owner (product owner), teams (team), backlog items (backlog items), planned releases (planned releases), sprints (sprints). Scrum terminology forms the starting point of a ubiquitous language. Every organization that buys a subscription registers itself as a tenant, another term for our common language .





. , DDD . , , DDD . , . , .





? . , . . :





  • , .





  • .





  • .





  • .





  • .





  • .





. , .





:

ยซ ยป . , , . :





  • , .





  • , .





  • , .





  • , .





Product . , Product, BacklogItem, Release, Sprint , . . UML- .





public class Product extends ConcurrencySafeEntity {

    private Set<BacklogItem> backlogItems;
    private String description;
    private String name;
    private ProductId productId;
    private Set<Release> releases;
    private Set<Sprint> sprints;
    private TenantId tenantId;
    ...

}

      
      



Figure:  1. The product is modeled as a very large assembly.
. 1. Product .

, - . , .  , . , , . () , .





:





  • , , Product c 1 .





  • BacklogItem . 2.





  • Release , , Product 1.





. .





. , . . .





. ? , -. , . , .





:

, 2. . ProductId, Product-.





Figure:  2. Product and related concepts are modeled as separate aggregates.
. 2. Product .

Product. :





public class Product ... {
    	...
      public void planBacklogItem(
        String aSummary, String aCategory,
        BacklogItemType aType, StoryPoints aStoryPoints) {
      		...
      }
    	...
      public void scheduleRelease(
        String aName, String aDescription,
        Date aBegins, Date anEnds) {
      		...
      }

      public void scheduleSprint(
        String aName, String aGoals,
        Date aBegins, Date anEnds) {
        	...
      }
      ...
}

      
      



. Product, , โ€“ void. :





public class Product ... {
    	...
      public BacklogItem planBacklogItem(
        String aSummary, String aCategory,
        BacklogItemType aType, StoryPoints aStoryPoints) {
      		...
      }
  
      public Release scheduleRelease(
        String aName, String aDescription,
        Date aBegins, Date anEnds) {
        	...
      }

      public Sprint scheduleSprint(
        String aName, String aGoals,
        Date aBegins, Date anEnds) {
        	...
      }
      ...
}

      
      



. . , , :





public class ProductBacklogItemService ... {
     ...
     @Transactional
     public void planProductBacklogItem(
           String aTenantId, String aProductId,
           String aSummary, String aCategory,
           String aBacklogItemType, String aStoryPoints) {

           Product product =
                   productRepository.productOfId(
                                 new TenantId(aTenantId),
                                new ProductId(aProductId));

           BacklogItem plannedBacklogItem =
                  product.planBacklogItem(
                            aSummary,
                            aCategory,
                            BacklogItemType.valueOf(aBacklogItemType),
                            StoryPoints.valueOf(aStoryPoints));
          
          backlogItemRepository.add(plannedBacklogItem);
      }
      ...
}

      
      



, . BacklogItem, Release Sprint .





. , , . , , , - . , , .





, . , .





โ€” -, . . , . . . :





c = a + b

, , = 2 b = 3, 5. , 5, . , , .





AggregateType1 {
    int a; int b; int c;
    operations...
}

      
      



, , , - , . , , . , .





. , , . โ€“ , , , , . .





( ) . .





, , , . , . . . , , .





: ? , , . , , , , , . - , .





, , ? , (lazy loading). , . , , . , . , , . , .





, 3. 0..* . . , . . , , . .





Figure:  3. Product model.  Several large collections are loaded during many simple operations.
. 3. Product. .

. , , .





, , ยซยป. , , , . , (root entity), / (object value).





, (, ) ? : , . , Product name description. , . , , . -, .





, , , . , , . , , , . , . . - , .





. , . , . , . Order OrderItem . , - , . , .





, , . . , , , .





, . , - . , . , . , , .





, -, , - , . 4. , , , . . , . , , , -.





Figure:  4. Shows concurrent access to data between three users.  They try to access the same two instances of aggregates, resulting in a large number of transaction failures.
. 4. . , .

, , . , , . ?





, , , , . - (eventual consistency) . , , , . , , . . , .








All Articles