Skip to content

andrewwebber/cqrs-typescript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cqrs-typescript

###CQRS implementation in typescript.

Components avaliable:

  • Event Sourcing
    • Interfaces for creating versioned events
    • Base classes for creating an event sourced aggregates and raising versioned events
  • Command processing
    • Interfaces for creating commands
    • Command bus for sending events
    • Redis based command bus using 'rpoplpush'
    • Command handling registry for signing up for command types
  • Event processing
    • Interfaces for creating events
    • Event bus for sending events
    • Redis based event bus using 'rpoplpush'
    • Event handling registry for signing up for command types

View the tests for more examples on usage.

####Event Sourcing

/// <reference path="mocha.d.ts"/>
/// <reference path="should.d.ts"/>

import CQRS = require('cqrs-typescript');
import should = require('should');
should.equal('actual', 'actual');

interface ICreditAccountEvent extends CQRS.IVersionedEvent{
    amount:number
}

interface IDebitAccountEvent extends CQRS.IVersionedEvent{
    amount:number
}

class BankAccount extends CQRS.EventSourced{
  constructor(id :string){
    super(id);
    this.balance = 0;
  }

  balance : number;

  credit(amount:number) : void {
      this.update({
          name: 'CreditAccount',
          amount: amount,
          version: -1,
          sourceId: ''
      });
  }

  debit(amount:number) : void{
    this.update({
        name: 'DebitAccount',
        amount: amount,
        version: -1,
        sourceId: ''
      });
  }

  private onCreditAccount(e : ICreditAccountEvent):void{
    this.balance += e.amount;
  }

  private onDebitAccount(e : IDebitAccountEvent):void{
    this.balance -= e.amount;
  }
}

var account : BankAccount;

describe('extending from "EventSourced" to create a "bank account"', function() {
  it('should be ok to create one once supplying an id ', function() {
    account = new BankAccount('1');
  });

  it('should be ok to credit the account to 100 by raising an event',function(){
    account.credit(100);
    account.balance.should.be.exactly(100);
    account.getEvents().length.should.be.exactly(1);
    account.getVersion().should.be.exactly(1);
  });

  it('should be ok to credit the account to 150 by raising an event',function(){
    account.credit(50);
    account.balance.should.be.exactly(150);
    account.getEvents().length.should.be.exactly(2);
    account.getVersion().should.be.exactly(2);
  });

  it('should be ok to debit the account by 100 by raising an event',function(){
    account.debit(100);
    account.balance.should.be.exactly(50);
    account.getEvents().length.should.be.exactly(3);
    account.getVersion().should.be.exactly(3);
  });

  it('should be ok to load a bank account from an event stream',function(){
      var accountFromEvents = new BankAccount('1');
      var events = account.getEvents();
      accountFromEvents.loadFromEvents(events);
      accountFromEvents.balance.should.be.exactly(account.balance);
      accountFromEvents.getVersion().should.be.exactly(account.getVersion());
  });
});

####Redis based event sourcing repository

var account = new BankAccount('2');
account.credit(100);
account.credit(100);
account.debit(50);
account.credit(100);
account.debit(200);
account.balance.should.be.exactly(50);

var provider = new CQRS.RedisEventSourcedRepository({ host: "127.0.0.1", port:6379});
it('should connect to a specified Redis server',function(done){
  provider.connect((error)=>{
    should.equal(error, null);
    done();
  });
});

it('should be able to persist an event stream for an given aggregate id',function(done){
  var events = account.getEvents();
  events.length.should.be.exactly(5);
  provider.saveEventsByAggregateId(account.getId(),events, (error)=>{
      should.equal(error, null);
      done();
  });
});

it('should be able to retrieve an event stream by aggregate id and recreate an aggregate instance',function(done){
  provider.getEventsByAggregateId(account.getId(),(error, events)=>{
    should.equal(error, null);
    events.length.should.be.exactly(5);

    var accountFromEvents = new BankAccount(account.getId());
    accountFromEvents.loadFromEvents(events);
    accountFromEvents.balance.should.be.exactly(account.balance);
    done();
  });
});