Search Results for

    Show / Hide Table of Contents

    Practicing DDD (Domain Driven Design) with Intent Architect

    Intent Architect and our standard modules have extensive support for modelling DDD (Domain Driven Design) systems for both anemic and rich domain approaches.

    A great introduction and explanation of these concepts is covered in our A Deep Dive into Domain Modeling Webinar.

    Rich Domain Support in Intent Architect

    Our designers have full support for Rich Domain modelling, in particular:

    • You can model Operations (behaviors) on Domain Classes.
    • In the Services designer Commands, Queries and Service Operations can be mapped directly to Class Operations.

    Rich Domain Modelling Example

    In this example we will enable "Private Setters" for our domain entities in our Application, model a Class with a Constructor and Operation in the Domain Designer and then in the Services Designer model CRUD Commands and Queries for interacting with our domain entity.

    This example assumes you are using an already created Application or you've created a new Application with the "Clean Architecture .NET" application template:

    Clean Architect .NET Application Template selection

    Enabling "private setters"

    In order to make it impossible to anemically update domain entities, you can enable the private setters option which makes all domain entities have private setters as opposed to the default of them being public.

    Go to the Settings of the Application:

    Application Settings context menu option

    Under the Domain Settings section, enable Ensure Private Property Setters and press the Save Changes button.

    Domain Settings

    Create a domain entity

    In the Domain designer, create a Customer entity and add a Name attribute onto it:

    Customer entity with Name attribute

    Right-click the Customer and select the Add Constructor option:

    The Add Constructor context menu option

    Right-click the Constructor and select the Map Constructor... option:

    The Map Constructor context menu option

    Select the Name Attribute and then press the DONE button:

    The Map Constructor screen

    With the constructor created and mapped, you can run the Software Factory and observe the following happening in the Customer.cs file:

    The Customer.cs file

    A public constructor has been created and because we mapped the Name, it has automatically created a parameter which sets the property.

    Also note that a protected constructor has been created so that Entity Framework is still able to re-hydrate entities it reads from your database.

    Similarly to creating the constructor, we will use the Add Operation context menu option:

    The Add Operation context menu option

    We'll name it Update and then use the Map Operation... context menu option:

    The Map Operation context menu option

    Again, just select the Name attribute and press DONE:

    The Map Operation dialogue

    When running the Software Factory, observe that it adds a method to the Customer:

    Update method being added to the Customer

    Create CQRS CRUD Operations

    In the Services designer, right-click the Services Package and select the Create CQRS Operations option:

    The Create CQRS Operations context menu option

    Observe that the CreateCustomerCommand is mapping to the constructor while the UpdateCustomerCommand is mapped to the Update operation:

    Customer diagram showing commands, queries and mappings

    To see the details of a mapping, we can right-click it and choose the appropriate menu option, for example for an [update] we can select the Map Entity Update option:

    Map Entity Update context menu option

    Observe how the Command is mapped to the Update Operation while the Name field is mapped to its parameter:

    Entity Update Mapping screen

    And when the Software Factory is run you will see that the generated implementation is passing the field's value to the method:

    UpdateCustomerCommandHandler.cs file

    Conclusion

    We have now created Rich Domain behaviors on our Domain entity and mapped our services to interact with them.

    Advanced Example: Modeling Aggregates and Composite Entities

    In certain scenarios, you may need to model an aggregate root (for example, an Order) that manages a collection of associated entities (for example, OrderLine). This section illustrates how to structure such a domain relationship in Intent Architect using private setters, map these structures through the Services designer, and expose operations to manage the aggregate.

    Designing the Aggregate and Composite Entities

    1. Create the Domain Classes
      In the Domain Designer, model a class named Order and another named OrderLine. Establish a one-to-many relationship so that Order contains a collection of OrderLine entities.
      Order and OrderLine in the domain

    2. Use Private Setters and a Dedicated Constructor
      To keep the domain model cohesive and prevent anemic updates, introduce a constructor that accepts a list or enumeration of OrderLineDO data objects. Within that constructor, create a collection to map these objects into actual OrderLine entities:

    public class Order
    {
        private List<OrderLine> _orderLines = [];
    
        public Order(int refNo, IEnumerable<OrderLineDO> orderLines)
        {
            RefNo = refNo;
            _orderLines = orderLines.Select(s => new OrderLine(s.Description, s.Amount)).ToList();
        }
    }
    

    Order class constructor code

    1. Instantiate the Aggregate via a Command
      In the Services Designer, configure a CreateOrderCommand that links to this constructor. Fields for critical properties, like RefNo and the list of OrderLineDO, can then be mapped automatically.
      CreateOrderCommand created in Services designer

    Managing Existing Aggregates

    After the Order entity is created, you can add or modify OrderLine entities:

    1. Define a Domain Operation
      For instance, create an AddOrderLine operation on the Order entity. This method can accept parameters (e.g., amount, description, quantity) and build a new OrderLine (this assumes your OrderLine has a constructor accepting: amount, description, quantity).
    public void AddOrderLine(decimal amount, string description, int quantity)
    {
        _orderLines.Add(new OrderLine(amount, description, quantity));
    }
    

    AddOrderLine operation in the domain designer

    1. Expose the Operation with a Command
      Create an AddOrderLineOrderCommand (or similar) in the Services Designer. Map its parameters to the AddOrderLine operation.
      CQRS CRUD Operations menu

    2. Command Handler
      Intent Architect will generate the handler logic, retrieving Order from the repository and invoking AddOrderLine:

    public async Task Handle(AddOrderLineOrderCommand request, CancellationToken cancellationToken)
    {
        var order = await _orderRepository.FindByIdAsync(request.Id, cancellationToken);
        if (order is null)
        {
            throw new NotFoundException($"Could not find Order with Id '{request.Id}'");
        }
        order.AddOrderLine(request.Description, request.Amount);
    }
    

    This advanced example extends the concepts introduced earlier by demonstrating how to create aggregates with composite entities and expose domain operations through commands in Intent Architect—helping maintain a cohesive, rich domain model.

    • Edit this page
    ☀
    ☾
    In this article
    Back to top Copyright © 2017-, Intent Software Pte Ltd.