-
Notifications
You must be signed in to change notification settings - Fork 0
Blog
A series of blog post that will go live
let us quickly define what is CQRS and the next progression of the framework we are designing. CQRS, CQ, CQS AH! omg so many command and queries definitions, let's talk about them and define what the purpose of our CQRS will be about.
command and query separation, is a very simple principle; and works at a method level. but it's simple command change the state of your system, and a query should return data. Simple right! 😂
"Asking a question should not change the answer." (Meyer, Bertrand. "Eiffel: a language for software engineering" (PDF). p. 22. Retrieved 16 December 2014.) Example:
public class Person
{
/*
A simple visitor pattern implementation will call the appropriate method based on the Command parameter
*/
public void Command(ChangePersonNameCommand command) { /* .... */ }
public void Command(ChangePersonAgeCommand command) { /* .... */ }
public void Command(ChangePersonAddressCommand command) { /* .... */ }
public Person Query(Guid personId) { /* .... */ }
}
Command:
[User(Change the person's name)]: =Action[Click Save]> [Application] =Send> [Command(ChangePersonNameCommand)]
# Query:
[Application] =Request> --------------------------
| GetPerson(Person:Guid) |
[Application] <Response= ____________________________
command query responsibility separation, works at an object level, or at a datastore level. States that a command can write to a database, and a query should read from that database. in a true CQRS system you will post a command get 200OK response and then query another endpoint to retrieve the update from the datastore. CQRS is still a very simple concept as above. You should one model that is responsible for write to your datastore and one model responsible for reading from your datastore. Example:
[CommandHandler]
public class Person
{
PersonQuery():base("AppendOnlyPersonDataStore") { /* .... */ }
/*
A simple visitor pattern implementation will call the appropriate method based on the Command parameter
*/
public void Command(ChangePersonNameCommand command) { /* .... */ }
public void Command(ChangePersonAgeCommand command) { /* .... */ }
public void Command(ChangePersonAddressCommand command) { /* .... */ }
}
[QueryHandler]
public class PersonQuery
{
PersonQuery():base("ReadOnlyPersonDataStore") { /* .... */ }
public Person Query(Guid personId) { /* .... */ }
public Person Query(Guid personId, int secondsBehind) { /* .... */ }
}
Command:
[User(Change the person's name)]: =Action[Click Save]> [Application] =Send> [Command(ChangePersonNameCommand)] =Append> [DataStore:Writer]
# Query:
[Application] =Request> --------------------------
| GetPerson(Person:Guid) | <Read= [DataStore:Reader]
[Application] <Response= ____________________________
Note you don't need two datastore for this work, your can hide the mechanism to the underlying datastructure behind a factory and depending on the key or query you return a client based on the type 🤷♂
phew! that was a lot.....
so our next step of our application is to start to adding the initial coding blocks for the CQRS pattern, and this will eventually (no pun intended) lead to use supporting event sourcing (ES) in our CQRS pre-post processing steps. Read set code! Next time design 😃
in this series i want to jot down some ideas i have in my head of how we are going to implement Event Sourcing in our application, but i want to focus mostly on a particular tier of ES: EventStoreClient, EventReader, EventWriter, EventStoreConfiguration
Objective of each object:
- Connects to a document datastore can retrieve or write document to it's data store
- keeping in mind we should have an online and offline data store, just in case event store goes down
- we will need a way to do something like fakeeventstore just in case we need it for testing
- a reader that is responsible for reading documents from the connected datastore and returning them
- this doesn't return an object just documents
- write an event or events to a connected datastore
- how do we create an instance of it?
- configuration for EventStoreClient
- the configuration
- replays an event return an to an aggregate model
- given an aggregate stream replays all the events for the aggregate and create an instance of the object
in this post I want to do a high level overview what is in my head about how this app loader will work and what is my idea of a BFF is for this app
currently we have many apps in the pipeline: calendar reminders goal tracker journal entry daily tweets -- third party apps (future) etc...
the idea is we will let the user login, once the user logins we will then load the app loader this will be a screen that will host all the apps for this user. these apps will be just three things (icon image, app name, app ui endpoint) when the user interacts with the app the app loader will make a request to the ui endpoint, whether this is a asp.net controller or just end point is out of the scope of this discussion what we are concerned about is the html div and this content that is returned.
but the interesting concept is the environment we load these apps should things like client id or other required fields the ui can use to make the request
in the beginning of the app I was really confused about which datastore to go with eventstore, ravendb, postgres + marten.
after looking at a lot of the projects and seeing all the functionality they provide I was really loving the ravendb and the admin panel is total love 😍
but in terms of functionality there is a lot of wiring up todo in the beginning to get it to act like an eventstore. that felt like that would be ideal if there is wasn't something already there, but the biggest thing that drew my attention and made me finally decide to go with eventstore was I wouldn't have to worry about sagas, with the built in event subscription provider.
but I will write a small public project that use both the above listed projects just for fun