mastering-abp-framework-259-336

Part 3: Implementing Domain-Driven Design

  • Focuses on Domain-Driven Design (DDD).
  • Introduces DDD overall.
  • Deep dives into the implementation (based on ABP Framework) by showing and explaining explicit rules and examples.
  • Includes chapters on:
    • Understanding Domain-Driven Design
    • DDD – The Domain Layer
    • DDD – The Application Layer

9 Understanding Domain-Driven Design

  • ABP Framework's Main Goal:
    • Introduce an architectural approach to application development.
    • Provide the necessary infrastructure and tools to implement that architecture with best practices.
  • Domain-Driven Design (DDD) is a core part of ABP Framework's architecture offering.
  • ABP's startup templates are layered based on DDD principles and patterns.
  • ABP concepts directly map to the tactical patterns of DDD:
    • Entity
    • Repository
    • Domain Service
    • Domain Event
    • Specification
  • Part 3, Implementing Domain-Driven Design, consists of three chapters.
    • Focus on practical implementation details rather than theoretical and strategic approaches.
    • Examples based on the EventHub project introduced in Chapter 4.
    • Different examples are shown for scenarios that the EventHub project lacks.
  • Explicit rules and concrete code examples for implementing DDD.
  • Overview of DDD core technical concepts:
    • Introducing DDD
    • Structuring a .NET solution based on DDD
    • Dealing with multiple applications
    • Understanding the execution flow
    • Common principles of DDD
  • Technical Requirements:
    • Source code for the EventHub project from GitHub: https://github.com/volosoft/eventhub
    • IDE/editor (e.g., Visual Studio) to build and run ASP.NET Core solutions.
    • ABP CLI installed (explained in Chapter 2).

Introducing DDD

  • DDD is a software development approach for complex needs, connecting the software's implementation to an evolving model.
  • Suitable for complex domains and large-scale applications.
  • Not always necessary for simple, short-lived CRUD applications.
  • ABP doesn't force implementing all DDD principles in every application.
  • Following DDD principles in complex applications helps build a flexible, modular, and maintainable codebase.
  • DDD focuses on core domain logic rather than infrastructure details, which are generally isolated from the business code.
  • Implementing DDD is closely related to object-oriented programming (OOP) principles.
    • Understanding of OOP and SOLID principles is essential.
      • Single Responsibility
      • Open-Closed
      • Liskov-Substitution
      • Interface Segregation
      • Dependency Inversion
DDD Layers
  • Layering is a common principle for organizing software solutions to reduce complexity and increase reusability.
  • DDD offers a four-layered model:
    • Domain Layer
      • Contains essential business objects.
      • Implements core, use case-independent, reusable domain logic.
      • Doesn't depend on any other layer.
      • All other layers directly or indirectly depend on it.
    • Application Layer
      • Implements use cases of the applications.
      • Uses the domain layer's objects to perform these use cases.
      • A use case is typically an action taken by the user through the UI.
    • Presentation Layer
      • Contains UI components (views, JavaScript, CSS).
      • Does not directly use the domain layer or database objects.
      • Uses the application layer.
      • For every UI action, there is a corresponding application layer method.
    • Infrastructure Layer
      • Depends on all other layers.
      • Implements abstractions defined by these layers.
      • Separates business logic from third-party libraries and systems (e.g., database or cache providers).
      • Each layer has a responsibility and contains various building blocks.
Building Blocks
  • DDD is mostly related to designing your business code by focusing on the domain you are working on.
  • Business logic is separated into two layers:
    • Domain Layer
    • Application Layer
  • Presentation and infrastructure layers are implementation details.
    • Implemented based on best practices of specific technologies (e.g., Entity Framework).
Domain Layer Building Blocks:
  • Entity:
    • A business object with a state (data) and business logic that works with the properties of this entity.
    • Has a unique identifier (ID) to distinguish it from others.
    • Two entities with different identifiers = different entities, even with identical properties.
    • Example: Event and Organization entities in the EventHub solution.
  • Value Object:
    • Identified by its state (properties).
    • Doesn't have an identifier.
    • Two value objects are the same if all their properties are the same.
    • Generally simpler than entities and typically implemented as immutable.
    • Examples: address, money, or date.
  • Aggregate and Aggregate Root:
    • An aggregate is a cluster of objects (entities and value objects) bound together by an aggregate root object.
    • The aggregate root is responsible for keeping the aggregate objects valid.
    • Implements and coordinates the business logic on these objects.
    • Example: Event entity is the aggregate root of the Event aggregate, containing tracks and sessions as sub-collections.
  • Repository:
    • A collection-like interface used by the domain and application layers to access the persistence system.
    • Hides the complexity of the database provider from the business code.
  • Domain Service:
    • A stateless service (class) that implements core business rules.
    • Useful for domain logic that depends on multiple aggregate types or external services.
    • Gets/returns domain objects and is generally consumed by application services or other domain services.
  • Specification:
    • A named, reusable, testable, and combinable filter applied to business objects to select them based on specific business rules.
  • Domain Event:
    • A way of informing other services in a loosely coupled manner when a domain-specific event occurs.
    • Useful for implementing side effects across multiple aggregates.
Application Layer Building Blocks:
  • Application Service:
    • A stateless service (class) that implements the use cases of the application.
    • Typically gets and returns data transfer objects and its methods are used by the presentation layer.
    • Uses and orchestrates the domain layer objects to perform a specific use case.
    • A use case is typically implemented as a transactional (atomic) process.
  • Data Transfer Objects (DTO):
    • Used to transfer data (state) between the presentation and application layers.
    • Doesn't contain any business logic.
  • Unit of Work (UOW):
    • A transaction boundary.
    • All state changes (typically database operations) in a UOW must be implemented as atomic.
    • Committed together on success or rolled back together on failure.
  • The big picture and core building blocks of DDD are important to understand, which is why they are introduced in brief here.
  • The next few chapters will use them in practice and explain their implementation details.
  • How ABP places the layers and building blocks into a .NET solution will be explained next.

Structuring a .NET solution based on DDD

  • Explains how a .NET solution can be layered based on DDD.
  • Starts with the simplest possible solution structure.
  • Explains how ABP's startup solution template evolved into its current structure.
  • Explanation of project purposes inside ABP's startup solution.

Creating a Simple DDD-Based .NET Solution

  • Creation of four projects in our .NET solution.

  • Assuming building a Customer Relationship Management (CRM) solution.

    • Acme is the company name, and Crm is the product name in this example.
    • A separate C# project is created for each layer.
    • .NET projects perfectly fit into layers as they can physically separate the codebase into different packages.
    • A class/type in a project can directly use other classes/types in the same project.
    • A class/type cannot use a class/type in another project unless you explicitly define the dependency by referencing the other project.
  • Example Components:

    • Acme.Crm.Domain
      • C# class library project.
      • Contains a Product class (aggregate root entity) and an IProductRepository interface(repository abstraction).
      • The Product class represents a product and has properties such as Id, Name, and Price.
      • IProductRepository has methods to perform database operations for products, such as Insert, Delete, and GetList.
    • Acme.Crm.Infrastructure
      • C# class library project.
      • Contains the CrmDbContext class (the EF Core data context), which maps the Product entity to a database table.
      • Also contains the EfProductRepository class, which implements the IproductRepository interface.
    • Acme.Crm.Application
      • C# class library project.
      • Contains ProductAppService (application service) along with methods to create, update, delete, and get a list of products.
      • This service internally uses the IProductRepository interface and the Product entity (the domain objects).
    • Acme.Crm.Web
      • ASP.NET Core MVC (Razor Pages) web application.
      • Has a Products.cshtml page (and a related JavaScript file) that renders the product data on the UI and allows you to manage (create, edit, and delete) the products.
      • It internally uses ProductAppService to perform the actual operations.
  • Project Dependencies:

    • Acme.Crm.Domain has no dependencies.

      • The domain layer has a minimal dependency and is abstracted from the infrastructural details.
    • Acme.Crm.Infrastructure has a dependency on the Acme.Crm.Domain project.

      • Needs to access the Product class to map it to a database table.
      • Implements the IProductRepository interface.
    • Acme.Crm.Application has a dependency on the Acme.Crm.Domain project.

      • Uses the IProductRepository repository and the Product entity to perform the use cases.
    • Acme.Crm.Web depends on the Acme.Crm.Application project.

      • Uses the application service (ProductAppService).
      • Also references the Acme.Crm.Infrastructure project.
      • The application needs the infrastructure layer at runtime to use the database.
      • An alternative structure will be discussed in the Separating the hosting from the UI section to get rid of that dependency.
  • A minimalistic layering of a DDD-based solution.

  • How ABP's startup solution has evolved will be explained.

Evolution of the ABP Startup Solution

  • ABP's startup solution is more complex than the four-project solution.
  • The same solution created with the ABP startup template using the abp new Acme.Crm CLI command.
Introducing the EntityFrameworkCore Project
  • The minimalistic DDD solution contains the Acme.Crm.Infrastructure project, which implements all infrastructural abstractions and integrations.
  • An ABP solution has a dedicated Entity Framework Core integration project (Acme.Crm.EntityFrameworkCore).
    • It's good to create separate projects for major dependencies, especially for database integration.
  • The infrastructure layer can be split into multiple projects.
  • ABP startup template has no such major dependency.
    • The only infrastructure project is the Acme.Crm.EntityFrameworkCore project.
  • If your solution grows, you can create additional infrastructure projects.
  • Changing the Acme.Crm.Infrastructure project's name to Acme.Crm.EntityFrameworkCore.
Introducing the Application Contracts
  • The Acme.Crm.Application project contains the application service classes.
  • The Acme.Crm.Web project references the Acme.Crm.Application project to use these services.
  • Problem: The Acme.Crm.Web project indirectly references the Acme.Crm.Domain project (over the Acme.Crm.Application project).
  • Exposes business objects in the domain layer to the presentation layer and breaks the abstraction and true layering.
  • The ABP startup template separates the application layer into two projects:
    • The Acme.Crm.Application.Contracts project, which contains the application service interfaces (such as IProductAppService) and the related DTOs (such as ProductCreationDto).
    • The Acme.Crm.Application project, which contains the implementations of the application services (such as ProductAppService).
  • Advantages of introducing contracts (interfaces) for the application services:
    • The UI layer (the Acme.Crm.Web project here) can depend on the service contracts without depending on the implementation, and therefore the domain layer.
    • You can share the Acme.Crm.Application.Contracts project with a client application to rely on the same service interfaces and reuse the same DTO classes without sharing your business layers.
  • The EventHub reference solution reuses the Application.Contracts project between the UI and the HTTP API applications.
    • Easily sets up a tiered architecture where the application layer and the presentation layer are hosted in different applications yet share service contracts.
  • By separating the application contracts project.
    • The Acme.Crm.Web project now only depends on the Acme.Crm.Application.Contracts project and should always use the application service interfaces to perform the user interactions.
    • The Acme.Crm.Web project still depends on the Acme.Crm.Application and Acme.Crm.EntityFrameworkCore projects since we need them at runtime.
  • Separating the application contracts from the implementation brings a small problem that we will solve in the next section.
Introducing the Domain Shared Project
  • Once the contracts have been separated, we can no longer use the objects of the domain layer inside the contracts project because they have no reference to the domain layer.
  • We shouldn't use these entities and other business objects in the application service contracts anyway – we should use DTOs instead.
  • We may still want to reuse some types or values defined in the domain project.
  • For example, we may want to reuse a ProductType enum in a DTO class or depend on the same constant value for the product name's maximum length.
  • We don't want to duplicate such code parts, but we also can't add a reference to the Acme.Crm.Domain project from the Acme.Crm.Application.Contracts project.
  • Introduce a new project to declare such types and values.
  • Name this new project Acme.Crm.Domain.Shared since this project will be part of the domain layer and shared with the rest of the solution.
  • This project won't contain so many types in practice, but we still don't want to duplicate these types.
  • The new Acme.Crm.Domain.Shared project is used by the Acme.Crm.Domain and Acme.Crm.Application.Contracts projects.
    • In this way, directly or indirectly, all the other projects in the solution can use the types in that new project.
  • The fundamental layers of the ABP startup solution are complete.
  • The ABP startup solution has three more projects, which we will discuss.
Introducing the HTTP API Layer
  • The ABP startup solution has two HTTP-related projects.
    • First, the Acme.Crm.HttpApi project contains the API Controllers (that is, the REST APIs) of the solution.
      • Separating the API from the UI would be better to organize and develop the solution.
      • Separating the HTTP API layer as a class library project makes some advanced scenarios possible by allowing them to be reused.
      • The EventHub solution uses the HTTP API layer as a proxy in the UI layer (the UI and HTTP API are hosted in different applications in that solution).
    • The second HTTP API-related project is Acme.Crm.HttpApi.Client.
      • A class library project that can be used in more advanced scenarios.
      • You can use this library from a client application (it can be your application or a third-party .NET client) to easily consume your HTTP APIs.
      • It uses ABP's Dynamic C# Client Proxy system.
      • Most of the time, you don't make any changes to this project, but it automagically works.
      • The EventHub solution uses this technique to perform HTTP API requests from the UI application.
  • By adding two new projects for the HTTP API layer, we now have eight projects in the solution.
    • The Acme.Crm.HttpApi and Acme.Crm.HttpApi.Client projects depend on the Acme.Crm.Application.Contracts project because the server and client share the same contracts (application service interfaces).
    • The Acme.Crm.Web project depends on the Acme.Crm.HttpApi project since it serves the APIs at runtime.
  • Not every application needs to have HTTP APIs (that is, REST APIs).
    • You can even remove this project from the solution.
    • You can move your API controllers to the Acme.Crm.Web project and discard the Acme.Crm.HttpApi project.
Understanding the Database Migrator Project
  • There is one more project named Acme.Crm.DbMigrator.
    • A console application that can be used to apply EF Core code-first migrations to the database.
    • It is a utility application and not part of the essential solution.
Test Projects in the Solution
  • There are six more projects in the solution under the test folder.
    • Unit/integration tests projects separately configured for each layer.
    • One of them (Acme.Crm.HttpApi.Client.ConsoleTestApp) demonstrates how to consume HTTP APIs using the Acme.Crm.HttpApi.Client project.
  • The solution structure provided is the architectural model, followed by all the pre-built official ABP application modules.
    • This model makes it possible to reuse the application modules in various scenarios, thanks to its flexibility and modularity.
  • An additional project that can be used to separate the hosting from the UI application will be discussed.
Separating the Hosting from the UI
  • One annoying thing in the architectural model shown is that the Web project references the Application and EntityFramework projects.
  • None of the pages/classes in the Web project directly use classes in these projects.
  • Since the Web project is the project that runs the application, we needed to reference these projects to make them available at runtime.
  • This structure is not a big problem, so long as you do not accidentally leak your domain and database layer objects into the presentation (web) layer.
  • If you are worried and do not want to set development time dependencies for these runtime dependencies, you can add one more project, Acme.Crm.Web.Host.
  • With this change, the Acme.Crm.Web project becomes a class library project rather than a final application.
    • It only contains the presentation layer pages/components of the application.
    • It does not contain the Startup.cs, Program.cs, and appsettings.json files.
  • The Acme.Crm.Web.Host project becomes responsible for hosting by bringing all the projects together at runtime.
  • It doesn't contain any application UI page or component.
  • This design is better since:
    • It gracefully extracts the hosting configuration details from the UI layer,
    • Removes the runtime dependencies,
    • Keeps it more focused.
  • A solution with multiple projects, and with less code in each project, is a better approach than a single project with everything in one place.
  • You understood the roles that each project has in the ABP startup template, so you should be more comfortable while developing your solutions.
  • The EventHub reference solution will be briefly revisited from a DDD perspective.

Dealing with Multiple Applications

  • It is a good starting point for a well-architected software solution.
  • Sets up the layers properly, with a single domain layer and a single application layer (which is used by a single web application).
  • In the real world, software solutions may be more complex.
  • You may have multiple applications (on the same system) or may need to separate your domain into multiple sub-domains to reduce the complexity of each sub-domain.
  • DDD addresses the design of complex software solutions.
  • One of the main purposes of separating the business logic into domain logic and application logic is to correctly organize your code base when there are multiple applications in your solution.
  • When you have multiple applications, you have multiple application layers.
    • Each of these layers implements the application-specific business logic of the related application, yet still shares the same core domain logic by using the same domain layer.
  • The EventHub project has two web applications:
    • The main website that is used by end users.
    • The admin (back office) application, which is used by system administrators.
  • These applications have different user interfaces, different use cases, different authorization rules, and different performance, localization, caching, and scaling requirements.
  • Separating these differences into two application layers helps us isolate these application-specific business and infrastructure requirements from each other.
  • These applications share the core business logic that we don't want to duplicate across the applications, meaning that two application layers use the same domain layer.
  • When we have multiple applications, separating the business logic between the application and domain layers becomes even more important.
  • Leaking domain logic into the application layers duplicates it.
  • Placing application-specific logic in the domain layer leads you to coupling the business logic of different applications and writing many conditional statements to make the domain layer usable by these applications.
  • Both of these situations make your codebase buggy and difficult to maintain.
  • Domain logic versus application logic separation is important.
  • How a web request is executed in a DDD-based application will be learned.

Understanding the Execution Flow

  • Introduced many building blocks and their descriptions, as well as how these building blocks are placed in layers in a .NET solution.
  • An HTTP request is executed in a typical web application that has been layered based on DDD.
  • A request starts with a request from a client application.
    • The client can be a browser that expects an HTML page (with its CSS/JavaScript files) or a data result (such as JSON).
      • A Razor Page can process the request and return an HTML page.
  • If the application making the request is another kind of client (such as a console application), you probably respond to the request from an HTTP API (an API controller) endpoint and return a plain data result.
  • The MVC page (in the presentation layer) processes the UI logic, may perform some data conversions, and delegates the actual operation to a method of an application in the application layer.
  • The application service may take a DTO, implement the use case logic, and return a resulting DTO to the presentation layer.
  • The application service internally uses the domain objects (entities, repositories, domain services, and more) to coordinate the business operation.
  • The business operation should be a unit of work, meaning that it should be atomic, and all the database operations in a use case (typically, an application method) should be committed or rolled back together.
  • The presentation and application layers typically implement the cross-cutting concerns, such as authorization, validation, exception handling, caching, audit logging, and so on.
  • ABP Framework provides a complete infrastructure for all these cross-cutting concerns and automates them wherever possible.
  • It also provides proper base classes and practical conventions to help you structure your business components and implement DDD with best practices.
  • Some common principles of DDD will be discussed.

Understanding the Common Principles

  • DDD focuses on how you design your business code, caring about state changes and how the business objects interact, and how to preserve the data validity and integrity.
  • DDD doesn't care about reporting or mass querying; can duplicate data for read-only reporting purposes.
  • You are free to do anything, so long as you don't mix the infrastructure details with your business code.
  • DDD also doesn't care about the infrastructure details; you are expected to isolate your business code from these details with proper abstractions.
  • Two of these abstractions are especially important since they take a big place in your codebase:
    • Presentation technology
    • Database provider
Database Provider Independence
  • It is a good practice to abstract the database integration in a DDD-based software solution.
  • Your domain and application layers should be database and even ORM independent, in theory.
  • There are some good reasons behind this suggestion:
    • Your database provider (ORM or DBMS) may change in the future without affecting your business code, making it longer-lived.
    • Your domain and application layers become more focused on your business code by hiding the data access logic behind the repositories.
    • You can mock the database layer for automated tests more efficiently.
  • The ABP startup template follows this principle, not including references to the database provider from the domain and application layers.
  • ABP Framework already provides the infrastructure to implement the repository pattern easily.
  • The ABP startup template also comes with the database layer, which uses an in-memory database instance for automated tests.
  • The last two of these reasons are important and easy to apply with ABP Framework.
  • The first one is not so easy, and it seems like you make your business code ORM/database independent when you place your data access logic behind the repositories, but it is not that simple.
  • Assuming you are currently using EF Core with SQL Server (a relational database) and want to design your business code and entities so that you can easily switch to MongoDB (a document database) later, if you want to accomplish that, you must take the following into account:
    • You can't assume that you have the change tracking system of EF Core because the MongoDB .NET driver doesn't provide that feature, so you should always manually update the changed entities at the end of your business logic.
    • You can't add navigation or collection properties to your entity where these properties are types of other aggregates; you must strictly implement the aggregate pattern (as will be explained in Chapter 10, DDD – The Domain Layer) by respecting the aggregate boundaries; this restriction deeply affects your entity design and the business code that works on your entities.
  • Being database-agnostic requires care when it comes to designing the entity and affects your codebase.
  • Even if you try to do it, will it be truly database-independent (you may not know it before trying to switch)?
  • All ABP pre-built application modules are designed to be independent of the database provider, and the same business code works both on EF Core and MongoDB.
  • This is necessary since they are reusable modules and can't assume a database provider.
  • A final application can make this assumption.
  • I still suggest hiding the data access code behind the repositories, and ABP makes this very easy.
  • However, if you want to go with an EF Core dependency, I see no problem with that.
Presentation Technology-Agnostic
  • UI frameworks are the most dynamic systems in the software industry.
  • Plenty of alternatives, and the trending approaches and tools are rapidly changing.
  • Coupling your business code with your UI code would be a bad idea.
  • Implementing this principle is more important and relatively easier, especially with ABP Framework.
  • The ABP startup template comes with proper layering.
  • ABP Framework provides many abstractions that you can use in your application and domain layers without depending on ASP.NET Core or any other UI framework.

Summary

  • In this first chapter on DDD, we looked at the four fundamental layers and the core building blocks in these layers.
  • The ABP startup template is more complex than that four-layered structure.
  • You learned how the startup template evolved by one change at a time, and you understood the reasons behind these changes.
  • Regarding DDD, you learned that the business logic is separated into two layers: the application layer and the domain layer.
  • We discussed how to deal with multiple applications that share the same domain logic by referencing the EventHub example solution.
  • We then understood how an HTTP request is executed and passed through the layers in a typical DDD-based software.
  • Finally, we discussed isolating your application and domain layers from the infrastructure details, especially the database providers and UI frameworks.
  • This chapter aimed to show the big picture and the fundamental concepts of DDD.
  • The next chapter will focus on implementing domain layer building blocks, such as aggregates, repositories, and domain services.

10 DDD – The Domain Layer

  • The previous chapter was an overall view of Domain-Driven Design (DDD), where you learned about the fundamental layers, building blocks, and principles of DDD.
  • You also gained an understanding of the structure of the ABP solution and its relation to DDD.
  • This chapter completely focuses on the implementation details of the domain layer with a lot of code examples and best practice suggestions.
  • Here are the topics we will cover in this chapter:
    • Exploring the example domain
    • Designing aggregates and entities
    • Implementing domain services
    • Implementing repositories
    • Building specifications
    • Publishing domain events

Technical Requirements

  • You can clone or download the source code of the EventHub project from GitHub:
    https://github.com/volosoft/eventhub.
  • If you want to run the solution in your local development environment, you need to have
    an IDE/editor (such as Visual Studio) to build and run ASP.NET Core solutions. Also, if
    you want to create ABP solutions, you need to have the ABP CLI installed, as explained in
    Chapter 2, Getting Started with ABP Framework.

Exploring the Example Domain

  • The examples in this chapter and the next chapter will mostly be based on the EventHub solution.
  • So, it is essential to understand the domain first.
  • Chapter 4, Understanding the Reference Solution, has already explained the solution.
    • You can check it if you want to refamiliarize yourself with the application and the solution structure.
  • Explanation of the technical details and the domain objects.
Main Concepts of the Domain:
  • Event: The root object that represents an online or in-person event.
    • Properties: title, description, start time, end time, registration capacity (optional), and a language (optional).
  • An event is created (organized) by an Organization.
    • Any User in the application can create an organization and organize events within that organization.
  • Track: an event can have zero or more Tracks with a track name.
    • A track is a sequence of sessions.
    • An event with multiple tracks makes it possible to organize parallel sessions.
  • Session: A track contains one or more Sessions.
    • A session is a part of the event where attendees typically listen to a speaker for a certain length of time.
  • Speaker: A session can have zero or more Speakers.
    • A speaker is a person who talks in the session and makes a presentation.
    • Generally, every session will have a speaker.
    • There can be multiple speakers, or there can be no speaker associated with the session.
  • Any user in the application can Register for an event.
    • Registered users are notified before the event starts or if the event time changes.

Designing Aggregates and Entities

  • It is very important to design your entities and aggregate boundaries since the rest of the solution components will be based on that design.
What is an Aggregate Root?
  • An aggregate is a cluster of objects bound together by an aggregate root object.
  • The aggregate root object is responsible for implementing the business rules and constraints related to the aggregate, keeping the aggregate objects in a valid state and preserving the data integrity.
  • The aggregate root and the related objects have methods to achieve that responsibility.
  • The Event aggregate is a good example of aggregates.
The Event Aggregate design:
  • The Event object is the aggregate root here, with a GUID primary key.
    • It has a collection of Track objects (an event can have zero or more tracks).
  • A Track is an entity with a GUID primary key and contains a list of Session objects an event can have zero or more tracks.
    • A track should have one or more sessions.
  • A Session is also an entity with a GUID primary key and contains a list of Speaker objects.
    • A session can have zero or more speakers.
  • A Speaker is an entity with a composite primary key that consists of SessionId and UserId.
  • Event is a relatively complex aggregate.
    • Most of the aggregates in an application will consist of a single entity, the aggregate root entity.
      • The aggregate root is also an entity with a special role in the aggregate: it is the root entity of the aggregate and is responsible for sub-collections.
    • Entity includes both aggregate root and sub-collection entities.
Two Fundamental Properties of an Aggregate:

1. A Single Unit:

  • An aggregate is retrieved (from the database) and stored (in the database) as a single unit, including all the properties and sub-collection entities.
  • Example: To add a new Session to an Event:
    1. Read the related Event object from the database with all the Track, Session, and Speaker objects.
    2. Use a method of the Event class to add the new Session object to a Track of the Event.
    3. Save the Event aggregate to the database together with the new changes.
  • Implement business rules to keep the aggregate objects consistent and valid.
  • DDD is for state changes. Mass queries/reports can be optimized as much as possible.
  • The Event.AddSession method internally checks whether the new session's start time and end time conflict with another session on the same track. Also, the time range of the session should not overflow the event time range. We may have other business rules, too. We may want to limit the number of sessions in an event or check whether the session's speaker has another talk in the