# Context

{% hint style="warning" %}
This project and documentation are work in progress
{% endhint %}

{% hint style="info" %}
Example code: [src/lib/context](https://github.com/jbreckmckye/node-typescript-architecture/tree/master/src/lib/context)
{% endhint %}

As discussed before, context is a parameter passed to all operations at runtime, providing implementations for the operation's side effects.

It's in the context module that we make all this fit together.

### Defining the Context type

Very simply, a context is a dictionary of objects. Each of these objects concerns a particular capability of the app, and typically each one is provided by its own adapter.

For instance, the community library app context looks as follows:

```typescript
import { BackendCtx } from './backend'
import { EventsCtx } from './events'

export {
  BackendCtx,
  EventsCtx
}

export type Context = {
  backend: BackendCtx,
  events: EventsCtx
}
```

So the `backend` is provided by one adapter, the `events` by another.

And `BackendCtx` (used for persistence) looks like this:

```typescript
export type BackendCtx = {
  bookStore: BookStore,
  loanStore: LoanStore,
  userStore: UserStore,
}

export type BookStore = {
  add:            (b: BookInput)     => Promise<Book>,
  find:           (i: UUID)          => Promise<Book|null>
}

export type UserStore = {
  add:            (u: UserInput)     => Promise<User>,
  remove:         (u: User)          => Promise<void>,
  find:           (i: UUID)          => Promise<User|null>
}

export type LoanStore = {
  takeLoan:       (l: LoanInput)     => Promise<Loan>,
  endLoan:        (l: Loan)          => Promise<Loan>,
  getUserLoans:   (u: User)          => Promise<Loan[]>,
  getLoan:        (b: Book)          => Promise<Loan|null>
}
```

When the lib is assembled into an app, it will require an adapter that provides an implementation of `BackendCtx` , for instance a `MongoDBAdapter`.

### The operation and adapter types

{% hint style="info" %}
You don't have to understand these pieces of code to use them... but it will help you develop a mental model of how an NTA app fits together.
{% endhint %}

Operations should be familiar by now. They are the functions that do useful things with domain entities:

```typescript
export type Operation <I, O> = (c: Context, i: I) => Promise<O>
```

That is, given an Input and an Output type, an Operation takes (Context, Input) and returns a Promise of Output. (We know it will be a promise because all operation functions are async).

The adapter type is new:

```typescript
export type ContextAdapter <C = Context, P = any> =
  <I, O> (op: Operation<I, O>, params?: P) =>
    Promise<{ ctx: Partial<C>, op: Operation<I, O> }>
```

This looks a tad complicated (luckily we only need write it once). Let's break it down:

* \[Line 1] A `ContextAdapter` works on types `C` and `P`.
  * `C` is the context being populated (by default the global `Context`, but can be overriden)
  * `P` is an object of any params the adapter needs *at runtime* (optional)
* \[Line 2] A `ContextAdapter` will be given an operation and those parameters.
  * The operation has an input and output, hence `I` and `O`.
  * The adatper's runtime params are optional, hence the `?: P`
* \[Line 3] It will return both a `ctx` (a partial context, for instance `{ backend }` ) and an `op` function that equals or wraps the operation passed in
  * The result of an adapter is wrapped in a Promise, because adapter functions are async.

What this means is that we can generate a part of the context on demand, when an operation is requested by a user. The adapter function uses any params to help it along, and populates a field in the Context. It can also wrap the operation function with its own code.

**Why would an adapter want to wrap an operation?** Consider the case of [database transactions](https://en.wikipedia.org/wiki/Database_transaction). You want a way to start a transaction and either commit or rollback, depending on whether the operation throws an error.

{% hint style="info" %}
Other use cases include error reporting, logging, and any kind of resource allocation you need to do around the operation.
{% endhint %}

**Why would an adapter need runtime parameters?** A use case might be something like transaction logging, where the request is assigned an ID and you want your adapter to pass this on to any internal APIs it calls.

**Why do adapters return promises?** So they can be written as async functions, making it easy to fit in any non-synchronous function calls.

### The mergeAdapter function

So, we have various adapters that can populate different fields on the `Context`. How can we combine them?

We need a `mergeAdapter` function. This should return a single adapter that, when called, invokes and combines the results of all the adapters originally passed to it.

Here's an example of `mergeAdapters` written in a recursive style:

```typescript
export function mergeAdapters <C = Context, P = any> (...args: ContextAdapter<C, P>[]): ContextAdapter<C, P> {
  // Separate out the first adapter
  const [first, ...rest] = args

  return async function adapter (op, params) {
    // The merged adapter
  
    // Evaluate the first adapter
    const result = await first(op, params)

    // No more? Short circuit
    if (rest.length === 0) {
      return result

    } else {
      // Otherwise: merge the rest and call what comes out (recurses)
      const restResult = await (mergeAdapters(...rest)(result.op, params))
      
      // Now merge the results
      return {
        ctx: {
          ...result.ctx,
          ...restResult.ctx
        },
        op: restResult.op // later adapters wrap ops returned by earlier adapters
      }
    }
  }
}
```

{% hint style="info" %}
If you find recursion unfamiliar, take a look at [this guide](https://codeburst.io/learn-and-understand-recursion-in-javascript-b588218e87ea) by Brandon Morelli.
{% endhint %}

What's important is that you can call it with a set of individual adapters to create a larger adapter:

```typescript
const contextAdapter = mergeAdapters(
  postgres, // provides { backend }
  rabbitmq  // provides { events }
)           // provides { backend, events }
```

### Applying the adapter with wrapAdapter

Now we need a way to combine the merged adapter with an operation. We'll use this at the last stage of defining the library. Enter `wrapAdapter`:

```typescript
export function wrapAdapter <I, O, CA extends ContextAdapter> (adapter: CA, operation: Operation<I, O>) {
  // Takes an adapter and operation, and returns a function
  // that takes (operation input) and (adapter params)
  
  return async function (input: I, adapterParams?: any) {
    const { ctx, op } = await adapter(operation, adapterParams)
    return op(ctx as Context, input)
  }
}
```

Again, let's step through this piece by piece:

* WrapAdapter is a function that operates on types `I`, `O` and `CA`.
  * `I` is the input to the operation
  * `O` is the output of the operation (wrapped in a promise)
  * `CA` is any ContextAdapter
* It is given an adapter and an operation
* It returns a function that takes the input of the operation, and any runtime parameters required by the adapter
* Internally, when called, the function calls the adapter and creates a context and wrapped operation
* The wrapped operation is called with the context and input, and the result is returned

This is how our dependency injection works. We use higher order functions to replace the boilerplate of generating and passing context ourselves. To the user of an operation wrapped with `wrapAdapter`, they seem to just be passing an input and receiving an output. They have no idea about the context.

Now we can put all this together to create the **library root**. Read on!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jbreckmckye.gitbook.io/node-ts-architecture/step-by-step/context.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
