Skip to content

Latest commit

 

History

History
79 lines (57 loc) · 2.89 KB

_05-entities.md

File metadata and controls

79 lines (57 loc) · 2.89 KB

Entities

To create an entity... You guessed it! We use the boost tool:

boost new:entity <name of the entity> --fields fieldName:fieldType --reduces EventOne EventTwo EventThree

For instance, running the following command:

boost new:entity Order --fields shippingAddress:Address orderItems:"Array<OrderItem>" --reduces OrderCreated

It will generate a class in the src/entities folder with the following structure:

@Entity
export class Order {
  public constructor(readonly id: UUID, readonly shippingAddress: Address, readonly orderItems: Array<OrderItem>) {}

  @Reduces(OrderCreated)
  public static createOrder(event: OrderCreated, previousOrder?: Order): Order {
    return event.order
  }
}

Entities are not shown in the diagram because they're just a different view of the data in the events store.

Entities represent domain model objects, that is, something that can be mapped to an object with semantics in your domain. Entities only exist conceptually, they're not explicitly stored in any database, but generated on the fly from a list of related events.

Booster creates snapshots of the entities automatically under the hoods to reduce access times, but the developer doesn't has to worry about that.

Examples of entities are:

  • A Cart
  • An Account
  • A User

As you can see, entities are also regular TypeScript classes, like the rest of the Booster artifacts.

Take a look, entities have a special reducer function decorated with @Reduces, that will be triggered each time that a specific kind of event is generated.

All projection functions receive:

  • The event
  • A possible previous state (note the ? meaning that there could be no previous state. i.e. when the app is just starting)

And it always must return a new entity. This function must be pure, which means that it cannot perform any side effects, only create a new object based on some conditions on the input data, and then return it.

Reading Entity "state"

@Command({
  authorize: 'all',
})
export class MoveStock {
  public constructor(readonly productSKU: UUID, readonly fromLocationId: UUID, readonly toLocationId: UUID, readonly quantity: number) {}

  public handle(register: Register): void {
    const productStock = fetchEntitySnapshot('ProductStock', this.productSKU)

    if (productStock.locations[this.fromLocationId].count >= this.quantity) {
      // Enough stock, we confirm the movement
      register.events(new StockMovement(this.productSKU, this.fromLocationId, this.toLocationID, quantity))
    } else {
      // Not enough stock, we register this fact
      register.events(new FailedCommand({
        command: this,
        reason: `Not enough stock in origin location`
      ))
    }
  }
}

Booster provides a handy fetchEntitySnapshot method to check the value of an entity from any handler method in order to make domain-driven decisions.