# Domain

## Description

The domain encapsulates the entire business logic of your application. It handles validation and authorization; it decides when and how entities change.

In the world of **Event Sourcing**, changes are made by appending new events to a database. If we want to know the current state of an entity, we load all events for the given ID and run them through a **Reducer**. The **reducer** interprets the events and returns to us the current state.

Events are generated by things called **Actions**, they are responsible for deciding if a change should be allowed. If allowed, the **action** will return one or more events which describe the change. If not allowed, the **action** will throw an error.

**Actions** and **reducers** are pure functions. They cannot mutate data or read from external data sources. All external information must be passed in as one of the parameters. The returned value should always be a new object.

In other words, the domain does not know anything about the outer layers of the application. Given some input, they return some output. That's all they can do.

{% hint style="warning" %}
**Actions** and **reducers** are pure functions. They cannot mutate data or read from external data sources.
{% endhint %}

**Actions** are given two parameters: the current state of the entity, and a payload. The current state is calculated by running existing events through the **reducer**. The payload is an arbitrary object provided by the caller.

Based only on these two parameters, the action decides to accept or reject the command. **Let me say that again:** Any information required for the action to make its decision must be contained in the current state of the entity or in the given payload.

{% hint style="warning" %}
**Actions** are given the current state of the entity and a payload. Based on these two parameters, and *only* on these parameters, it decides if it should reject and if not, what kind of event to return.
{% endhint %}

For example, let's say that one of our business requirements is that you can only update an entity if you are the owner. We need to make sure that the payload contains the `userId` of the caller and the state contains the `userId` of the owner. Inside the **action** we compare them and throw an error if they don't match, like so:

```javascript
if (state.userId !== payload.userId) throw new Error('unauthorized')
```

### Actions

An object where keys are action names and values are functions which accept two parameters, `state` and `payload`, and return an array of one or more events. If the action is not allowed, it should throw an error object

```
module.exports = {
  someActionName: (state, payload) => {
    return [
    // ...events
    ]
  }
}
```

### Reducer

A function which accepts two parameters; an array of `events` and the `state` of the entity prior to the first event in the array (if the event array starts at the very first event then there is no previous state and the parameter should be omitted).

```
const initialState = {
  // ...some initial state
}
const reducer = (state, event) => {
  // compute and return new state
}
module.exports = (events, state=initialState) => events.reduce(reducer, state)
```

{% hint style="info" %}
As a performance optimization, we can snapshot entities at a certain version number. Given a snapshot, you need only to apply newer events to get the current state.
{% endhint %}

## Examples

### Basic

In this example there is only one **action**, `addTodo`, and the only requirements is that the title is not null. The **action** returns a new event with the type `TodoAdded`. The **reducer** knows that whenever it comes across a `TodoAdded` event, it should append a new todo with a matching title.

{% code title="actions.js" %}

```javascript
const actions = {
  addTodo: (state, payload) => {
    if (!payload.title) throw new Error('titleMissing')
    
    return [{
      type: 'TodoAdded',
      title: payload.title,
      at: Date.now(),
    }]
  }
}

module.exports = actions
```

{% endcode %}

{% code title="reducer.js" %}

```javascript
const initialState = {
  todos: []
}

const reducer = (state, event) => {
  switch (event.type) {
    case 'TodoAdded':
      return {
        todos: [
          ...state.todos,
          { title: event.title },
        ]
      }
      
    default:
      return state
  }
}

module.exports = (events, state=initialState) => events.reduce(reducer, state)
```

{% endcode %}

### Extended

In this example, we'll do something a little different.

Firstly, we have a `createWidget` action. Normally on 'create' **actions**, we check to make sure that an object with the same ID doesn't already exist. We can check this by making sure the current state is `null`.

Secondly, we make sure we have the `userId` of the user who is calling `createWidget` and we store is as part of the event. We do so, so that in future calls to `setTitle` we can make sure the calling user is authorized to perform the **action**.

Lastly, when the user calls `createWidget`, we are separating the change into two distinct events: `WidgetCreated` and `TitleSet`. Doing so allows us to reduce the scope of change for each event and simplify event handling in the **reducer**.

{% code title="actions.js" %}

```javascript
const actions = {
  createWidget: (state, payload) => {
    if (!!state) throw new Error('alreadyExists')
    if (!payload.userId) throw new Error('userIdMissing')
    if (!payload.title) throw new Error('titleMissing')
        
    return [{
      type: 'WidgetCreated',
      userId: payload.userId,
      at: Date.now(),
    }, {
      type: 'TitleSet',
      title: payload.title,
      at: Date.now(),
    }]
  },
  
  setTitle: (state, payload) => {
    if (state.userId !== payload.userId) throw new Error('unauthorized')
    if (!payload.title) throw new Error('titleMissing')

    return [{
      type: 'TitleSet',
      title: payload.title,
      at: Date.now(),    
    }]
  }
}

module.exports = actions
```

{% endcode %}

{% code title="reducer.js" %}

```javascript
const initialState = {}

const reducer = (state, event) => {
  switch (event.type) {
    case 'WidgetCreated':
      return {
        userId: event.userId,
        createdAt: event.at,
        updatedAt: event.at,
      }

    case 'TitleSet':
      return {
        title: event.title,
        updatedAt: event.at,
      }
                  
    default:
      return state
  }
}

module.exports = (events, state=initialState) => events.reduce(reducer, state)
```

{% endcode %}
