Straight.Core 1.0.1

.NET Core 2.0
NuGet\Install-Package Straight.Core -Version 1.0.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
dotnet add package Straight.Core --version 1.0.1
<PackageReference Include="Straight.Core" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Straight.Core --version 1.0.1
#r "nuget: Straight.Core, 1.0.1"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install Straight.Core as a Cake Addin
#addin nuget:?package=Straight.Core&version=1.0.1

// Install Straight.Core as a Cake Tool
#tool nuget:?package=Straight.Core&version=1.0.1

Straight.Core

A set of simple implementaion of architectural patterns like CQRS, Event Sourcing, Domain-Driven Design. Command and Query Responsibility Segregation (CQRS) is a pattern that segregates the operations that read data (Queries) from the operations that update data (Commands) by using separate interfaces.

With this Framework, We are focus on the "Domain", because resolve an Business issue is the only one way to improve your solution. All reasons, which bring to focus on another thing, are lost time.

Two principal has been used to simplify the using of this framework, the programation by "Convention" and the reflexion. The "convention" allow to protected all methods inside the Domain when you handle your command in your model or when the event has been apply. The framework load by reflexion all methods with name of "Handle" which ICommand object and all methods with name of "Apply" which contains IEvent object. All handle methods and apply methods can be private, these will be call by the base of AggregateBase.

Sample of Domain

This model is an Account, it is Write model for Account, his name is AggregatorAccount. Aggregator, because he aggregates all events which composed what it is.


public class AggregatorAccount : AggregatorBase<IDomainEvent>
{
    
    private IEnumerable Handle(CreateAccountCommand command)
    {
        var creator = new User(command.CreatorLastName, command.CreatorFirstName, command.CreatorUsername);
        yield return new AccountCreated(command.AccountKey, command.Login, command.Password, creator);
    }

    private void Apply(AccountCreated @event)
    {
        _creator = @event.Creator;
        _connectionInformation = @event.ConnectionInfo;
    }

    private IEnumerable<IDomainEvent> Handle(AddVisitCommand command)
    {
        command.CheckIfArgumentIsNull("command");
        command.House.CheckIfArgumentIsNull("Address");
        AddressHelper.CheckMandatory(
            command.House.Address.Street,
            command.House.Address.City,
            command.House.Address.PostalCode);
        var estateOfficier = new User(command.EstateOfficierLastName,
            command.EstateOfficierFirstName,
            command.EstateOfficierUsername);
        if (IsInCurrentMeet(command.MeetDate))
        {
            throw new DateAlreadyExistException(command.MeetDate);
        }

        yield return new VisitAdded(command.House, estateOfficier, command.MeetDate);
    }

    private void Apply(VisitAdded @event)
    {
        _lastModifier = @event.EstateOfficier;
        _allMeetDates.Add(@event.MeetDate);
    }
}

His read model, this model will be stimulate when the bus raises all events generated by an command.

public class Account : ReadModelBase<IDomainEvent>, IAccount
{
    public User Creator { get; private set; }
    public User LastModifier { get; private set; }
    public int VisitCounter { get;private set; }
    public IHouse LastHouseVisited { get; private set; }
    public IOfficer LastOfficer { get; private set; }

    private void Apply(AccountCreated @event)
    {
        Creator = @event.Creator;

    }
    
    private void Apply(VisitAdded @event)
    {
        LastModifier = @event.EstateOfficier;
        VisitCounter++;
        LastHouseVisited = @event.House;
        LastOfficer = @event.Officier;
    }

}

Command Handler ?

public class AccountCommandHandler : ICommandHandler<CreateAccountCommandDto>
{
    private readonly IDomainEventStore<IDomainEvent> _repository;
    private readonly IModelConverter _converter;

    /// This constructor is built by the IoC and all dependecies has been injected by the IoC too
    public AccountCommandHandler(IDomainEventStore<IDomainEvent> repository, IModelConverter converter)
    {
        _repository = repository;
        _converter = converter;
    }

    public void Handle(CreateAccountCommandDto command)
    {
        command.CheckIfArgumentIsNull(nameof(command));
        command.Customers.CheckIfArgumentIsNullOrEmpty(nameof(command.Customers));
        command.Creator.CheckIfArgumentIsNull(nameof(command.Creator));        
        // it is possible to replace that by _repository.GetById<AggregatorAccount>(command.Id); and inside your repository you create your account.
        // I prefere keep the controle when I create a new instance and when I load an instance, and the second argument is "Segragation of responsability", 
        // when you "Get" an instance, you do not need to create a new instance.   
        var account = new AggregatorAccount();
        account.Update(new CreateEmployeAccountCommand
        {
            CreatorFirstName = command.Creator.FirstName,
            CreatorLastName = command.Creator.LastName,
            CreatorUsername = command.Creator.Username,
            Customers = command.Customers.Select(c => _converter.ToModel<Customer>(c)).ToList()
        });
        _repository.Add(account);
    }
}

Your repository can be File, Database, dataobj serialization etc.

Bus or not Bus ?

Sure ... BUS !!! 😃

Provide with this framework it is "InMemoryBus" If you implement IBus, you can inject RabbitMQ for example.

How I can call my Command Handler ?

    [Route("api/[controller]")]
    [ApiController]
    [ApiVersion("1.0")]
    public class AccountController : ControllerBase
    {
        private IBus bus;

        public AccountController(IBus bus, IReadModelRepository<User> userRepo)
        {
            this.bus = bus;
            this.userRepo = userRepo;
        }
        
        [HttpPost]
        public IActionResult Post([FromBody] CreateAccountCommandDto command)
        {
            if(!command.IsValid)
            {
                return BadRequest();
            }
            var creator = userRepo.Get(this.User.Identity.Id);
            if(creator == null)
            {
                return this.BadRequest("Not authorized");
            }
            bus.Publish(command);
            return Ok();
        }
    }
}
Product Versions
.NET net5.0 net5.0-windows net6.0 net6.0-android net6.0-ios net6.0-maccatalyst net6.0-macos net6.0-tvos net6.0-windows
.NET Core netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1
Compatible target framework(s)
Additional computed target framework(s)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.1 385 8/4/2019
1.0.0 331 8/3/2019