I'm practicing the CQRS pattern and I can't understand it. I need to execute a command to create an entity, which in turn has navigation properties .It turns out that when creating, I request data from the database by ObjectId. But it turns out I'm doing query in command.
`public async Task<ResponseBase> Handle(CommandCreateItem request, CancellationToken cancellationToken){
var dto = request.Item;
var newItem = new Item();
_color = await _context.Colors.FindAsync(dto.ColorId);
_seasonItem = await _context.SeasonItems.FindAsync(dto.SeasonItemId);
_itemType = await _context.ItemTypes.FindAsync(dto.ItemTypeId);
var price = decimal.Parse(dto.Price, NumberStyles.Any, CultureInfo.InvariantCulture);
var countItem = uint.Parse(dto.CountItem);
var characteristic = new CharacteristicItem
{
Color = _color, SeasonItem = _seasonItem,ItemType = _itemType, Id = Guid.NewGuid(),Item = newItem
};
newItem = new Item
{
Id = Guid.NewGuid(),
Title = dto.Title,
ArticleNumber = dto.ArticleNumber,
Description = dto.Description,
NumberOfSales = 0,
CountItem = countItem,
Price = price,
CharacteristicItem = characteristic,
};
await _context.CharacteristicItems.AddAsync(characteristic, cancellationToken);
await _context.Items.AddAsync(newItem, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
return new ResponseItemCreate(newItem.Id);
}`
Is this normal? How to do it right? After all, its essence is to share responsibility.
CodePudding user response:
when the concept of CQRS defines the segregation of commands for write operations and queries for reading operations, it doesn't mean that it must be so strict as you think.
There are reasons for that segregation of responsability. One of them for example is to escale write operations from read operations based on Business demands. Other, is to use different databases if you need.
You can find more information on this msdn documentation.
CodePudding user response:
TL;DR there's nothing wrong with reading from the source of truth when you execute command transactions. CQRS requires finding novel ways to service query and read activity from alternate sources.
Simply put, CQRS implies that reading data need not be done from the same data source, nor in the same manner that writing is done.
By adhering to this principle, CQRS allows systems which have more read activity (e.g. UI, query APIs etc) to scale far beyond an equivalent system which both reads and writes from a centralised data store (source of truth) would have otherwise allowed, e.g. from a single SQL RDBMS.
As most scaled out transactional (OLTP) systems are stateless, they maintain state in a persisted data store, it follows that most systems will need to read the current state (truth) to assert any preconditions and rules / validations before applying any changes (i.e. process Commands, requiring IO writes), in the same way systems doing all read and write I/O from centralised data store would have done.
So there is nothing wrong with your stateless, 'read before write' approach to processing Command transactions. The CAP theorem restricts your scaling options for write transactions while keeping the integrity of your data state.
The difference is for non-transactional read activity, e.g. Point reads or queries for UI, reports, analytics etc. which do not require absolutely fresh data. If reads can be done from a quicker 'cache' (often distributed, replicated, and updated asynchronously, hence eventually consistent
), then the overall scale of the system improves. These read caches (often called read sides or read stores) allow read-heavy systems to scale beyond the limitations of a shared read-write database.
The only way to avoid read-before write activity and retaining consistency and integrity from the the source database is to change the true source off storage and into into memory, e.g. Actor Model. However this has different challenges, such as routing (for scale) and failover / fault tolerance.
So for read-scalable, stateless CQRS systems, ensure your command writes are efficient and consistent, but spend most of your effort in finding novel ways to service read and query activity from caches. Reactive or Event-Driven Architecture is one way to help achieve this result.