1

I want to save an order with order items with prices in my write database. But firstly I need to fetch products with their prices because I don't trust a user - he could pass a lower price, that's why I don't want to add a Price property to CreateOrderDTO.
Where should I fetch product prices? In a command handler - if yes then from which database: read database or write database? Or maybe in a controller before sending a command?

The flow in my application is the following:

API - the controller:

[HttpPost]
public async Task<IActionResult> CreateOrder(CreateOrderDTO orderDto)
{
    var orderCmd = new CreateOrderCommand(orderDto.CustomerId, orderDto.OrderItems);
    await _mediator.Send(orderCmd);
}

The CreateOrderDTO model:

public class CreateOrderDTO
{
    public Guid CustomerId { get; set; }
    public List<CreateOrderItemDTO> OrderItems { get; set; }
}

public class CreateOrderItemDTO 
{
    public Guid ProductId { get; set; }

    public int Quantity { get; set; }
}

The command and command handler:

public class CreateOrderCommand : IRequest
{
    public Guid CustomerId { get; }
    public List<CreateOrderItemDTO> OrderItems { get; }

    public CreateOrderCommand(Guid customerId, List<CreateOrderItemDTO> orderItems)
    {
        CustomerId = customerId;
        OrderItems = orderItems;
    }
}

public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand>
{
    private readonly IEventStoreRepository _eventStoreRepository;

    public CreateCustomerCommandHandler(
        IEventStoreRepository eventStoreRepository)
    {
        _eventStoreRepository = eventStoreRepository;
    }

    public async Task<Unit> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
    {
        var order = new Order(Guid.NewGuid(), request.CustomerId);
        foreach (var item in request.OrderItems)
        {
            order.AddOrderItem(item.ProductId, item.Quantity, /* I need a price here */);
        }

        await _eventStoreRepository.Save(order);

        return Unit.Value;
    }
}

And my aggregate root:

public class Order : AggregateRoot
{
    public Guid Id { get; private set; }
    public Guid CustomerId { get; private set; }
    public List<OrderItem> OrderItems { get; private set; }

    public Order(Guid id, Guid customerId)
    {
        OrderItems = new List<OrderItem>();
    }

    public void AddOrderItem(Guid productId, int quantity, decimal price)
    {
      //...
    }
}

1 Answer 1

1

If yes then from which database: read database or write database?

TL;DR: usually the read database.


Recommended reading: Data on the Outside versus Data on the Inside

Basic riddle: does the fact that you are copying a price into an order mean that nobody is allowed to change the price in the product catalog?

After all, it takes sometime for the information to travel from there to here - by the time we have a copy of the information in memory, it might be nanoseconds out of date.

What's the impact to the business?

The usual answer is "not a lot".

A microsecond difference in timing shouldn’t make a difference to core business behaviors. -- Udi Dahan.

So in this common case, it's going to be OK to use a cached copy of the data, so long as the service level of cache updates is sufficient to meet the needs of the business. And that in turn means that read database/write database/local cache as the source of your data doesn't really matter very much -- it's just a matter of balancing different trade offs.

On the other hand, if it is critical to the business that the price in the order be exactly right - no latency allowed - then you are going to have to get the information from the write database (because all of the other sources can be nanoseconds behind) and you are going to have to make sure that information isn't changing while you work.


Another thing that you can sometimes do is replace concepts like "the current price" with "the price at the current local time". So when the order is placed we write down the local time, and then we can ask the product service what the price was at the time we wrote it down.

Immutability Changes Everything is a good starting point for this idea.

Notice the difference: "what's the price now?" is a question that gets different answers, depending on how long information takes to move through the system. "what was the price then?" always gets one of two answers - either the authoritative answer or "I don't know (yet)".

1
  • Thank you, this is a project only for me to learn DDD so a price doesn't have to be up-to-date. So I can simply query a read database and use a read model to fetch product prices in my command handler? I thought I can use only a event store database in a command handler? Commented Nov 20, 2022 at 17:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.