Aggregate Command Handlers

Extend AbstractAggregate

Then, specify command types handled by the aggregate (static get handles(): string[]) and define command handlers for each of them:

const { AbstractAggregate } = require('node-cqrs');

class UserAggregate extends AbstractAggregate {

  /** 
    * A list a of commands handled by the aggregate. 
    * Corresponding subscriptions will be established by the AggregateCommandHandler
    */
  static get handles() {
    return [
      'signupUser',
      'changePassword'
    ];
  }

  /**
    * Creates an instance of UserAggregate
    *
    * @param {object} options
    * @param {string} options.id - aggregate ID
    * @param {object[]} options.events - past aggregate events
    */
  constructor({ id, events }) {
    super({
      id,
      events,
      state: new UserAggregateState() 
    });
  }

  /**
    * "signupUser" command handler.
    * Being invoked by the AggregateCommandHandler service.
    * Should emit events. Must not modify the state directly.
    * 
    * @param {any} payload - command payload
    * @param {any} context - command context
    */
  signupUser(payload, context) {
    if (this.version !== 0) 
      throw new Error('command executed on existing aggregate');

    const { profile, password } = payload;

    // emitted event will mutate the state and will be committed to the EventStore
    this.emit('userSignedUp', { 
      profile, 
      passwordHash: hash(password)
    });
  }

  /**
    * "changePassword" command handler
    */
  changePassword(payload, context) {
    if (this.version === 0)
      throw new Error('command executed on non-existing aggregate');

    const { oldPassword, newPassword } = payload;

    // all business logic validations should happen in the command handlers
    if (!compareHash(this.state.passwordHash, oldPassword))
      throw new Error('old password does not match');

    this.emit('userPasswordChanged', {
      passwordHash: hash(newPassword)
    });
  }
}

results matching ""

    No results matching ""