2
votes

I'm looking for advice on how to structure namespaces in a CQRS structured application.

Currently the command-side and the query-side are in the same namespace in each bounded context, but as complexity is growing, it is starting to create problems.

Currently the structure has the following folders which each contains the implementation:

Application
+ Api
+ Cli
+ Web
Domain
+ Action (Command and Command Handlers in one - we are not using a CommandBus) 
+ Event
+ Model
+-- Project
  |-- Project.file
  |-- ProjectRepository.file
Infrastructure
+ Consumer (Projections and ProcessManagers)
+ EventStore
+ Persistance (Denormalized read side)
+-- Project
  |-- SqlProjectRepository.file
Common (Supporting namespace)

The issue is now that the Domain Model currently contains both the entities and event sourced aggregate root which are essentially only part the query-side and command-side, respectively.

There is no overlap in the aggregates of the query-side and command-side.

In a refactoring to a separation, where should the slice be made?

Suggestion 1

A full slice resulting in a query and a command-side which means that even the Application layer has read and write side.

Suggestion 2

A slice which is only made on the Domain layer so that the query side contains the (quite anemic) entities of the read model and the command-side holds events, event sourced aggregate root and more.

Please make a 3rd suggestion, if mine do not apply. Thanks.

2
Please see my answer below, just wanted to take up an additional comment, you're Application layer is wrong for me, not unusual - but wrong. Your "Application" is not API, Cli or Web - they're all just implementation layers (User or Application Interfaces), your application is the business logic that happens after the API, Cli and Web calls.mrdnk
Thanks for this comment. Basically that's an easy fix, because those UI sections are separated and can just be have their namespace changed. This leaves the Application layer quite empty since the Actions contain the Business Logic but they are currently part of the Domain layer - as recommended in much literature. Do you recommend that they move to the Application layer?nicolaib
If you look at my answer below, the Application is the main folder where you make your separation. Imo, Application should contain two folders - Command and Query. Then within each of those, you have Services (Handlers - depending on the implementation - validates commands, checks Auth etc, asks the domain questions), Domain (Command Models / Domain Models etc), and Data (save state / load query model). The implementation of those 3 layers - will be different for Commands Vs Queries.mrdnk

2 Answers

2
votes

This is the approach I take - based on CQRS and DDD, noting that our DDD extends to having a separate solution (.NET - Web APIs for different domain bounded contexts).

Secondly, all our infrastructure code, auth handling, and shared code - is done in a private package manager, which removes some of the mess, of a single solution, we DI it in the StartUp (DI). Also note we use EntityFramework as our DB implementation.

However, given that, here is how I and my team, separate out CQRS and DDD.

+ App
+- Command
+-- Application.Command
+-- Data.Command.EntityFramework
+-- Domain.Command

+- Query
+-- Application.Query
+-- Data.Query.EntityFramework
+-- Domain.Query

+ Build
+- Pipeline

+ Database
+- Data.Database.EntityFramework
+- Data.Database.Model

+ Test

Api (API app)
Cli
1
votes

Currently the structure has the following folders which each contains the implementation...

That's unfortunate.

You are likely to be happier with the maintenance burden if you arrange your name spaces such that things that change together are closer together. Your FrobMarbleRepository belongs in the frobmarble namespace, not in the repository namespace.

I don't think I agree with Jimmy Bogard's complete analysis here, but the lesson of focusing on features is important

https://jimmybogard.com/vertical-slice-architecture/

If you happen to need to use the same name for two different ideas within a single feature, then you might end up splitting that feature into two or more namespaces; on the other hand, the need to re-use a name might indicate you are actually dealing with more than one feature.