Coder Social home page Coder Social logo

domain-lib's Introduction

domain-lib

โš ๏ธ This repository is now archived. Please see https://github.com/DomainBlocks/domain-blocks, which is where this project has been moved to.


This library is intented to be a set of patterns and tools for use in projects that aim to apply concepts from DDD and event sourcing.

The goals of this library are to:

  • Provide tools that are more functional in nature than the alternatives.
  • Allow the domain layer to be kept clean of concerns that don't belong there.
  • Avoid opinionated and limiting framework-esque design decisions such as concrete inheritance.

What separates this from the myriad of similar libraries already out there?

A lot of other similar libraries tend to be some variation of the NEventStore.Domain project. This is certainly not a criticism of that project, and we all owe much to the concepts and patterns it has contributed to the community. However, there are areas in the design that domain-lib aims to improve upon.

For example, take the typical kind of interface seen for an aggregate root:

public interface IAggregate
{
    Guid Id { get; }
    int Version { get; }
    void ApplyEvent(object @event);
    ICollection GetUncommittedEvents();
    void ClearUncommittedEvents();
    IMemento GetSnapshot();
}

In order to use this functionality, aggregate roots must inherit from an abstract base class called AggregateBase. This introduces a number of concepts that, in our opinion, don't belong in the domain layer. For example all members of this interface, apart from Id and ApplyEvent, relate to concepts around persistance, which are concerns that really belong in the outer application and infrastructure layers. This interface is also opinionated around identity and versioning, along with the fact that an aggregate root must be mutable (i.e. ApplyEvent mutates the aggregate root's internal state).

The method ClearUncommittedEvents also poses an issue in the design. Generally speaking, there are two ways in which an aggregate root has events applied to it in order to update it's state:

  1. Through handling commands. Aggregate roots handle commands, which apply business rules and enforce invariants, and change state by "emitting" events.
  2. Through being rehydrated from the event log. A new aggregate root instance has historical events from the log applied to it to bring it up to the current state.

ClearUncommittedEvents only exists here as a workaround for the fact that, after the persistance layer hydrates the aggregate root from the event log, it will contain a list of the historical events in its uncommitted events collection. ClearUncommittedEvents exists so that the persistance layer can clear out those historical events so that they are not incorrectly appended to the event log again when the aggregate root is saved. This feels like a leaky abstraction.

We think there is a cleaner way to solve this problem which separates concerns, has fewer opinions, and allows for immutability.

domain-lib's People

Contributors

carlosrfernandez avatar daniel-smith avatar jonclare avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

jonclare

domain-lib's Issues

Support loading snapshot and subsequent events

Currently, the repository deals with loading a set of events that can be reconsituted into some aggregate state.
This is done by applying those events in order to an initial state.

Once we support persisting a snapshot of the state, see #13, then we can use this snapshot as the initial seed for the state, load subsequent events from the store and apply these to build the latest state.
The API of the method to load from snapshot and events can be similar to that that loads solely from events. The addition of the persisted state is needed.

The load method needs to be able to tell what version the snapshot state is for and then load any subsequent events.

Add EntityFramework adapter for projections

Rather than using raw SQL, we can use an EF DbContext to allow clients to deal with entities directly.

This would help when writing queries against the read model database. The same entities can be used.
One thing to be aware of is performance. If we're hydrating a read model from many events, we'll probably need to look into bulk operations.

Support saving snapshot of aggregate state

This could conceivably be done in another EventStoreDB stream.
When snapshotting is requred, the aggregate state can be serialized in a similar way to event serialization and written to a stream.
Stream metadata can be set to prune historical snapshots if the number of snapshots becomes too large.

Then, when reading, the latest event from the snapshot stream can be read before fetching the events to bring the aggregate state to the latest version.

Note that we will need a way of maintaining the maximum event number contained within the snapshot when saving the data, so as to be able to fetch this information and get only the events after this event number when loading.

Document projections library

  • Usage of library
  • Hooking up events to projections
  • Different types of projects that are supported out of the box
  • Creating your own projection types
  • Sql projections. Upserts, deletes and custom sql
  • Sql projections. Table creation - automatic and custom

Improve DB connection management in SqlProjections

Currently, database connections are created in a suitable DbConnector class (implementing IDbConnector)

When an event is handled and dispatched to projections, it may contribute to multiple SqlProjections. That is, we may write to multiple tables in the same database for one event. We would like all of these writes to happen in a single transaction so that we can update a database atomically from an event.

This transaction is controlled by a SqlContext class. The SqlContext class is associated with each event projection and a single SqlContext can be shared across multiple event projections. The SqlContext class uses the connection that is created by the DbConnector.

At the moment, to have things work properly the user must create a single DbConnection that's shared across all projections for the SqlContext to do its job properly.
The user must also ensure that the SqlDialect matches the DbConnector type to generate the correct SQL for the database.

All of this doesn't guide the consumer into the pit of success and we should enhance the API to better guide users to create DbConnectors and SqlDialects in the right way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.