Simple CQRS in ASP.NET Core

Eric Damtoft
Feb 22, 2019 · 3 min read

CQRS is the concept of separation of concerns between reading data and writing data. At a system level, this can mean that you can independently scale and optimize for reads and writes. At a code level, this generally means focusing on modeling user interactions within your domain, and modeling commands and queries rather than just exposing a data model.

Image for post
Image for post
Photo by Hannah Joshua on Unsplash

This approach fits the bill when working on a major refactor of DealerOn’s “Ignition” OpenID Connect service. The application handles sign-in and sign-out, but also provides an API for creating and managing users. Different user roles have permission to see a different information and have varying levels of access to the API. In the first few iterations of the refactor, I worked with a series of services which exposed read/write operations on a user, but no matter how much I cleaned up and pared down the code, it never quite felt right. Change tracking was awkward, and the variety of distinct scenarios for managing users lead to a rapidly ballooning mash up of services which handled specific flows, but with no real consistency between them.

This is where CQRS comes in. Modeling user interactions as commands and queries, rather than as reads/writes meant that commands could follow a uniform shape and leaves queries free to be optimized for each read model. This ended up providing a wide range of benefits, including implicit and consistent change tracking, easy testing, and improved consistency in the code. This post will focus mainly of the command side of CQRS and the patterns we used.

Building Blocks

Image for post
Image for post
Photo by Esther Jiao on Unsplash

We start with an abstract class. Each distinct user interaction is defined as a class that extends from .

An interface defines how we’ll handle each command.

Finally, an abstract class defines the outcome of a command. This allows you to define descriptive outcomes beyond just a success or an exception, and works nicely with C# 7 pattern matching to react to each distinct outcome.

Behind the Scenes

Image for post
Image for post
Command analytics and logging

We also added some simple middleware to automatically register handlers, manage database transactions for commands, and log and track the details and results of the commands while redacting sensitive information. This means that you get implicit logging, tracking, and analytics for any command that’s handled by the system. We also ship logged commands to Graylog, so it’s easy to do a quick query of how frequently users log in, failed login attempts, causes of errors, and more. I’ll cover the mechanics of this more in a future post as we generalize the framework a little bit more.

Handling Commands

Handlers represent core business logic, so given a set of requirements, they’re easy to read and write.

Testing

Since commands essentially define a scenario, and handlers define business logic, they are easy to write high value unit tests for. We can test a scenario for each user persona and assert that the outcome is what we expected.

Takeaways

Separating commands and queries is a great approach to explore when it feels like your code might not have the right levels of abstraction. Making clear commands which represent the scenarios in which users interact with your domain can make for readable, maintainable, and easily testable code.

DealerOn Dev

Syndicated from https://dev.to/dealeron

Thanks to sohjsolwin

Eric Damtoft

Written by

Software Architect at DealerOn

DealerOn Dev

Syndicated from https://dev.to/dealeron | On the DealerOn Dev Team, we strive to be the industry leader in code quality, innovation, and culture. The author’s views expressed in this publication are endorsed by DealerOn. The author’s views elsewhere may not be.

Eric Damtoft

Written by

Software Architect at DealerOn

DealerOn Dev

Syndicated from https://dev.to/dealeron | On the DealerOn Dev Team, we strive to be the industry leader in code quality, innovation, and culture. The author’s views expressed in this publication are endorsed by DealerOn. The author’s views elsewhere may not be.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store