Cronus is a lightweight framework for dispatching and receiving messages between microservices with DDD/CQRS in mind
License: Apache License 2.0
Batchfile 0.76%C# 99.24%
cronus's Introduction
Cronus is a lightweight framework for dispatching and receiving messages between microservices with DDD/CQRS in mind
Motivation
Building software is not an easy task. It involves specific domain knowledge and a lot of software infrastructure. The goal of Cronus is to keep the software engineers focused on the domain problems because this is important at the end of the day. Cronus aims to keep you away from the software infrastructure.
Usually you do not need a CQRS framework to develop greate apps. However we noticed a common infrastructure code written with every applicaiton. We started to abstract and move that code to github. The key aspect was that even with a framework you still have full control and flexibility over the application code.
Domain Modeling
To get out the maximum of Cronus you need to mark certain parts of your code to give hints to Cronus.
Serialization
ISerializer interface is really simple. You can plugin your own implementation but do not do it once you are in production.
The samples on this page work with Json and Proteus-protobuf serializers. Every ICommand, IEvent, ValueObject or anything which is persisted is marked with a DataContractAttribute and the properties are marked with a DataMemberAttribute. Here is a quick sample how this works (just ignore the WCF or replace it with Cronus while reading). We use Guid for the name of the DataContract because it is unique.
You can/should/must...
you must add private parameterless constructor
you must initialize all collections in the constructor(s)
you can rename any class whenever you like even when you are already in production
you can rename any property whenever you like even when you are already in production
you can add new properties
You must not...
you must not delete a class when already deployed to production
you must not remove/change the Name of the DataContractAttribute when already deployed to production
you must not remove/change the Order of the DataMemberAttribute when deployed to production. You can change the visibility modifier from public to private
ICommand
A command is used to dispatch domain model changes. It can be accepted or rejected depending on the domain model invariants.
Triggered by
Description
UI
It is NOT a common practice to send commands directly from the UI. Usually the UI communicates with web APIs.
API
APIs sit in the middle between UI and Server translating web requests into commands
External System
It is NOT a common practice to send commands directly from the External System. Usually the External System communicates with web APIs.
IPort
Ports are simple way for an aggregate root to communicate with another aggregate root.
ISaga
Sagas are simple way for an aggregate root to do complex communication with other aggregate roots.
Handled by
Description
IAggregateRootApplicationService
This is a handler where commands are received and delivered to the addressed AggregateRoot. We call these handlers ApplicationService. This is the write side in CQRS.
You can/should/must...
a command must be immutable
a command must clearly state a business intent with a name in imperative form
a command can be rejected due to domain validation, error or other reason
This is a handler where commands are received and delivered to the addressed AggregateRoot. We call these handlers ApplicationService. This is the write side in CQRS.
Triggered by
Description
ICommand
A command is used to dispatch domain model changes. It can be accepted or rejected depending on the domain model invariants
You can/should/must...
an appservice can load an aggregate root from the event store
an appservice can save new aggregate root events to the event store
an appservice can do calls to the ReadModel (not a common practice but sometimes needed)
an appservice can do calls to external services
you can do dependency orchestration
an appservice must be stateless
an appservice must update only one aggreate root. Yes, this means that you can create one aggregate and update another one but think twice
You should not...
an appservice should not update more than one aggregate root in single command/handler
you should not place domain logic inside an application service
you should not use application service to send emails, push notifications etc. Use Port or Gateway instead
This is a handler where commands are received and delivered to the addressed AggregateRoot. We call these handlers ApplicationService. This is the write side in CQRS.
Projection tracks events and project their data for specific purposes.
Triggered by
Description
IEvent
Domain events represent business changes which already happened
You can/should/must...
a projection must be idempotent
a projection must not issue new commands or events
You should not...
a projection should not query other projections. All the data of a projection must be collected from the Events' data
a projection should not do calls to external systems
IPort
Port is the mechanizm to do communication between aggregates. Usually this involves one aggregate who triggered an event and one aggregate which needs to react.
If you feel the need to do more complex interactions it is advised to use ISaga. The reason for this is that ports do not provide transperant view of a business flow because they do not have persistent state.
Triggered by
Description
IEvent
Domain events represent business changes which already happened
You can/should/must...
a port can send a command
ISaga/ProcessManager
When we have a workflow which involves several aggregates it is recommended to have the whole process described in a single place such as Saga/ProcessManager.
Triggered by
Description
IEvent
Domain events represent business changes which already happened
You can/should/must...
a saga can send new commands
IGateway
Compared to IPort, which can dispatch a command, an IGateway can do the same but it also has a persistent state. A scenario could be sending commands to external BC like push notifications, emails etc. There is no need to event source this state and its perfectly fine if this state is wiped. Example: iOS push notifications badge. This state should be used only for infrastructure needs and never for business cases. Compared to IProjection, which track events and project their data and are not allowed to send any commands at all, an IGateway store and track a metadata required by external systems. Also, IGateway are restricted and not touched when events are replayed.
Triggered by
Description
IEvent
Domain events represent business changes which already happened
You can/should/must...
a gateway can send new commands
Ecosystem
Legend
Name
Description
It is stable and it will continue to get support, maintenance and future development
The future is not clear. There are two possible paths from here - olympus or tartarus
This has been the prefered serialization with Cronus v2. However, there is a huge warm up performance hit with big projects which needs to be resolved. Despite this it works really fast. The implementation has small protocol changes
Builds projections dynamically. Very usefull for projects which just started and changes occur frequently. Later must be switch to other persister such as Cassandra