Skip to content

BoasE/BE.CQRS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CQRS

Common

This project represents a interpretation of the CQRS pattern, setting the focus on domainobjects, states and repositories. Although this project is already used in some real world projects like groupl I can't provide you any guarantues on functionality or support.

Currently a process of migration, documentation and samples creation is in progress. If you have any questions or wished please drop an issue here.

This project doesn't claim to be a prefect cqrs implementation. As most patterns CQRS has also many different real world interpretations.

Usage-Example

This is how a very basic domainobject looks like

public sealed class SessionDomainObject : DomainObjectBase
    {
        private readonly ILessonFactory lessonFactory;
        
        public SessionDomainObject(string id,ILessonFactory lessonFactory) : base(id,null)
        {
            Console.WriteLine("Creating object");

            this.lessonFactory = lessonFactory;
        }

        [Create]
        public void CreateSession(StartSessionForUserCommand cmd)
        {
            Precondition.For(cmd, nameof(cmd)).IsValidModel();
            
            ILesson lesson = lessonFactory.FromShortKey(cmd.LessonKey);

            IWorksheet worksheet = lesson.CreateWorksheet(new WorksheetArgument());
            
            
            RaiseEvent<SessionCreatedEvent>(@event =>
            {
                @event.Started = DateTimeOffset.Now;
                @event.UserId = cmd.UserId;
                @event.LessonKey = cmd.LessonKey;
                @event.WorksheetItems = worksheet.Items.ToEvent()
            });
        }
    }

Following an example for an api controler which triggers the creation process. The bus sends the command to a handler which creates the domain object, processes the events and persists the result

[HttpPost]
public async Task<IActionResult> Post([FromBody][Required] StartSessionModel model)
{
    Precondition.For(model, nameof(model)).NotNull().IsValidModel();

   await bus.EnqueueAsync(new StartSessionForUserCommand(Guid.NewGuid().ToString(), model.LessonKey, userId));

    return Accepted();
}

Persistance

Variant persistance implementations can be achieved by subclassing the domain object repository base. Currently two databases are implemented:

The EventStore implementation is already used in production scenarios , the mongodb is currentlyin a exeperimentell state.

Configuration-Examples

To get started have a look at the sample directory.

Adding the write part

MongoDb as EventStore, and asp.core serviceprovider for di

public static IServiceCollection AddCqrs(this IServiceCollection services, IConfiguration config)
    {
        string eventSecret = "0mDJVERJ34e4qLC6JYvT!$_d#+54d";
        var esconfig = new EventSourceConfiguration()
            .SetEventSecret(eventSecret)
            .SetDomainObjectAssemblies(typeof(DomainObjectSample).Assembly);

        string url = config["events:host"];
        string db = config["events:db"];
            
        services
            .AddServiceProviderDomainObjectAcitvator()
            .AddMongoDomainObjectRepository(()=>new MongoClient(url).GetDatabase(db))
            .AddConventionBasedInMemoryCommandBus(esconfig)
            .AddEventSource(esconfig);
        }

Adding the denormalizers

public static IServiceCollection AddDenormalizers(this IServiceCollection services, IConfiguration config)
{
    IMongoDatabase readDb = new MongoClient(readDburl).GetDatabase(readdb);

    var ctx = new DenormalizerContext(client, readDb);
    services.AddSingleton<IDenormalizerContext>(ctx);

   DenormalizerConfiguration deconfig = new DenormalizerConfiguration()
   .SetDenormalizerAssemblies(typeof(SampleDenormalizer).Assembly);

   services
       .AddServiceProviderDenormalizerActivator()
       .AddImmediateDenormalization()
       .AddDenormalization(deconfig);
   
   return services;
}
     

Ressources

To get started I strongly recommend to have a look at the awesome CQRS Webcasts by GregYoung.

Key-Concepts

DomainObject

The DomainObject is the center of each application logic. A very basic example is the Customer Domain Object in our first sample.

DomainObjectRepository

The DomainObjectRepository is responsible for persisting and reading the eventstreams of the domainobject. A very basic example is the usage of the MongoDomainObjectRepository which is registered first in the CQRSBooter in our first sample. Its usage is shown in the Program.cs of the same sample.

States

States are visitors that are iterating over stream of events to determine a desired state. The NameState for the Customer Domain Object determines the Name State for a customer. Accessing the NameState is also shown in the first sample's Program.cs.

Policies

Policies are specialized states which result in a boolean value. For example "IsCustomerActiveState"

CommandBus

CommandBus is used to send commands and in order to find their related Domain Objects and Processes that can handle the given Command. The CommandBus is registered in the CQRSBooter.cs of our second sample. After registration, it can be accessed in the CustomersController and enqueue Commmands.

EventSubscribers

EventSubscriber connect to eventstreams and provide notifications on new events. The [MongoDbEventSubscriber] is registered in the CQRSBooter.cs of our third sample.

Denormalizer

Denormalizers are working with eventsubscriber and are publishing new events to they registered eventhandlers e.g. for projecting informations in databases. By registering the CustomerDenormalizer in the CQRSBooter it can subscribe to the Notifications of the MongoDbEventSubscriber and create a CustomerReadModel.

About

Production implementation of the common domain object repository pattern in a CQRS context

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages