Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formalise concept of state-change rules within aggregate #60

Open
rhyslbw opened this issue Jan 28, 2016 · 1 comment
Open

Formalise concept of state-change rules within aggregate #60

rhyslbw opened this issue Jan 28, 2016 · 1 comment

Comments

@rhyslbw
Copy link
Member

rhyslbw commented Jan 28, 2016

A common business rule is to only allow a state change if the current state is equal to or is not equal to a single or group of values. It's currently tedious to protect against this, and as such I suspect may be just left out of testing or forgotten in the command handler.

Now

Donations.domain.test(Donations.Appeal)
  .given(closedAppeal.call(this))
  .when(
    new Donations.MakeAppeal({ targetId: this.appealId })
  )
  .expect([
    new Space.domain.Exception({
      thrower: 'Donations.Appeal',
      error: new Donations.InvalidAppealState('MakeAppeal', 'closed')
    })
  ]);

I suggest we take the focus of the message type ( commandMap and eventMap), and work on a pipeline for external state-change messages stateChangeMap? Mapping to a handler would be part of this, but only after enforcing state change rules as defined. It could throw a generic Space.Error like InvalidState.

As part of this we will need to abstract the mapping of internal state-change events into fields rather than just mapping an event type to a handler. This will be easier to test, and ensure dependencies are not being used to manipulate data once saved

@qejk
Copy link
Member

qejk commented Jan 29, 2016

We discussed with @rhyslbw few examples, however got nowhere atm:

(last been the first idea):

stateChangeMap: {
  'Businesses.UpdateBusienss': 
    denyWhen: [@STATES.closed, @STATES.canceled,@STATES.removed]
    handler: this._updateBusiness
    changeTo:  @STATES.newState
}
stateChangeMap: {
  'Businesses.UpdateBusienss': 
    allowWhen: (state) -> state is not in [@STATES.closed, @STATES.canceled,@STATES.removed]
    changeTo: (state) -> state
}
stateChangeMap: {
  'Businesses.UpdateBusienss': 
    denyWhen: [@STATES.closed, @STATES.canceled,@STATES.removed]
    changeTo:  @STATES.newState
}
stateChangeMap: {
  'Donations.CancelAppeal': 
    when: (state) -> state == Donations.Appeal.STATES.draft
    changeTo: Donations.Appeal.STATES.cancelled
}


stateChangeMap: {
  'Donations.UpdateAppealDraft': 
    when: (state) -> state != Donations.Appeal.STATES.draft
    changeTo: Donations.Appeal.STATES.cancelled
}
stateChangeMap: -> {
  'Donations.CancelAppeal': 
    denied/unallowed/blacklisted: []
    allowed/whitelisted: [Donations.Appeal.STATES.draft]
    changeTo/to: Donations.Appeal.STATES.cancelled
}

there’s maybe still potential to reduce boilerplate and confusion by providing a more expressive API, but I doubt we can achieve much past the state change validation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants