The CQS and CQRS patterns suggest dividing the operations of a program into commands and queries. A command mutates data, and a query fetches data. We can apply the Mediator pattern to break the tight coupling between the pieces of a program using CQS, like sending commands and queries.Dividing the program this way helps separate the different pieces and focus on the commands and queries that travel from a consumer through the mediator to one or more handlers. The data contract of commands and queries becomes the program’s backbone, trimming down the coupling between objects and tying them to those thin data structures instead, leaving the central piece (the mediator) to manage the links between them.On the other hand, you may find the codebase more intimidating when using CQS due to the multiple classes. It adds some complexity, especially for a small program like this. However, each type does less (having a single responsibility), making it easier to test than a more sizable class with many responsibilities.Now let’s see how CQRS can help us follow the SOLID principles:

  • S: Dividing an application into commands, queries, and handlers takes us toward encapsulating single responsibilities into different classes.
  • O: CQS helps extend the software without modifying the existing code, such as adding handlers and creating new commands.
  • L: N/A
  • I: CQS makes it easier to create multiple small interfaces with a clear distinction between commands, queries, and their respective handlers.
  • D: N/A

Now that we have explored CQRS, CQS, and the Mediator pattern, we explore the Marker Interfaces.

Code smell – Marker Interfaces

We used the empty ICommand and IQuery<TReturn> interfaces in the code samples to make the code more explicit and self-descriptive. Empty interfaces are a sign that something may be wrong: a code smell. We call those marker interfaces.In our case, they help identify commands and queries but are empty and add nothing. We could discard them without any impact on our system. On the other hand, we are not performing magic tricks or violating any principles, so they don’t harm but help define the intent. Moreover, we could leverage them to make the code more dynamic, like leveraging dependency injection to register handlers. Furthermore, I designed those interfaces this way as a bridge to the next project.Back to the marker interfaces, here are two types of marker interfaces that are code smells in C#:

  • Metadata
  • Dependency identifier

Metadata

Markers can be used to define metadata. A class “implements” the empty interface, and some consumer does something with it later. It could be an assembly scanning for specific types, a choice of strategy, or something else.Instead of creating marker interfaces to add metadata, try to use custom attributes. The idea behind attributes is to add metadata to classes and their members. On the other hand, interfaces exist to create a contract, and they should define at least one member; empty contracts are like a blank sheet.In a real-world scenario, you may want to consider the cost of one versus the other. Markers are very cheap to implement but can violate architectural principles. Attributes can be as cheap to implement if the mechanism is already implemented or supported by the framework but can cost much more than a marker interface, depending on the scenario. Before deciding, I recommend you evaluate the cost of both options.