Home > Enterprise >  In DDD with CQRS how to initialize aggregate with the list of sub-entities
In DDD with CQRS how to initialize aggregate with the list of sub-entities

Time:07-31

I'm building a DDD application with CQRS. In my aggregate I have a Budget aggregate root and BudgetedTransaction entity. Budget keeps track of the transactions, the list of BudgetedTransactions is the property of the Budget. In the database that's a 1-to-many relationship between the Budget and BudgetedTransaction.

My Budget class:

public class Budget : Entity, IAggregateRoot
{
    private readonly List<BudgetedTransaction> _budgetedTransactions;

    private Budget() { } //ef core

    public Budget(List<BudgetedTransaction> budgetedTransactions)
    {
        _budgetedTransactions = budgetedTransactions;
    }
}

I want to use CQRS and have separate repositories for reading and writing (Dapper for reading and EF for writing).

I'm struggling to understand how to retrieve and initialize the Budget root for writing purposes. Suppose I want to add a functionality of adding a new transaction.

Below is my Handle method in the command handler. How do I get from the list of DTOs to the list of actual entities, while not using EF for reads?

I'm coming to a conclusion that it is not possible and the reads with Dapper serve a different purpose (fetching persisted data to return from the API only)

Which would mean that my write repositories should really be both read and write repos, using EF, for the purposes of reading entities to make changes to them. Is that right? Is there a better solution?

public async Task<Guid> Handle(AddBudgetedTransactionCommand command, CancellationToken cancellationToken)
{
    var budgetWithTransactionsDto = await _budgetRepositoryRead.GetBudgetById(command.AddBudgetedTransactionRequest.BudgetId);

    var transactions = budgetWithTransactionsDto.BudgetedTransactions.ToList();

    var budget = new Budget(?);

    var transaction = new BudgetedTransaction(command.AddBudgetedTransactionRequest.BudgetId,
    command.AddBudgetedTransactionRequest.BudgetingCategoryId,
    command.AddBudgetedTransactionRequest.TransactionType,        
    command.AddBudgetedTransactionRequest.TransactionOccurrences,
    command.AddBudgetedTransactionRequest.StartDate,
    command.AddBudgetedTransactionRequest.Amount);

    budget.AddBudgetedTransaction(transaction);

    await _budgetRepositoryWrite.UnitOfWork.SaveEntitiesAsync(cancellationToken);

    budget.AddDomainEvent(new BudgetedTransactionAdded(transaction));
 
    return transaction.Id;
}

Method added to Budget:

public void AddBudgetedTransaction(BudgetedTransaction transaction)
{
    _budgetedTransactions.Add(transaction);
}

CodePudding user response:

Main issue here I think is from a misunderstanding of the CQRS pattern.

Short answer, it's impossible to don't have read methods in repositories, read and store entities (not DTO) is their role in DDD.

I want to use CQRS and have separate repositories for reading and writing (Dapper for reading and EF for writing).

With CQRS you have different models for writing and reading domain operation, a model to maintain data consistency while doing operation like creating a Budget, another one to optimize the execution of the query "Find all budget of users who are more than X years old and did more than Y transactions in 3 consecutive months"

As intended from the author CQRS is when you have different databases that are using "models" that helps you executing the operations your system needs.

Here some reference

  • Related