# Backend

## Folder structure

We use [NestJS](https://github.com/nestjs/nest) with the [Fastify](https://fastify.dev/) adapter as the framework for our backends. Each backend consists of two apps - api and cli - that you find under `apps` directory:

- `apps/api` the server-providing app that talks to the web application,

Additional micro-services can be added as separate apps in this directory.

Furthermore, you can find everything else (i.e. all building blocks of apps) inside the `lib` directory. Each folder inside `lib` contains a separate nest module that can be used by any app. For detailed description of each module, see the [Modules](#modules) section below.

## Data Flow

The backend implements a kind-of Model-View-Update (MVU) architecture. The data flow is as follows:

```
          +-------------+
          |  Front-end  |
          +-------------+
                ^
                | tRPC
                v
          +-------------+
          | Router/API  |
          +-------------+
            ^          \
           /            \ Dispatch
          /              \ actions
         / Business logic v
+-----------+      +----------+
|   View    |      |  Update  |
+-----------+      +----------+
        ^               /
         \             / Mutations
          \           v
         +-------------+              +------+
         |    Model    | <----------  | Seed |
         +-------------+  Mutations   +------+
                ^
                | Prisma
                v
         +-------------+
         |   Database  |
         +-------------+
```

#### Useful Links

- NestJS: https://github.com/nestjs/nest
- tRPC: https://trpc.io/
- Fastify: https://fastify.dev/
- PassportJS: https://duckduckgo.com/?q=passport+js&ia=web

---

## Cheat Sheet

### Changes to schema

The normal Prisma workflow applies here, i.e.:

1. Modify the schema in `schema.prisma`
2. Run `pnpm prisma migrate dev` to generate a migration
3. This will update the generated project files. Ensure that the changes are correct, esp. regarding any ejected files.
4. Run `pnpm prisma migrate deploy` to apply the migration to the production database

### Seeding

Seeding is done via seed migrations defined in `seed-data/src`.
At the application start, the seed service takes these migrations and checks if they have already been applied to the database.
In case they have not, it will apply them by triggering a corresponding seed action for each migration (enabling detailed logging).

Migrations are defined as `Actions`. We currently support three types of migrations:

1. Static data (`Action_Seed_Data`) - any constant data can be provided here in form of an array of `SeedData` objects.
   We use an array here to enable multiple subsequent steps, e.g. to first create items and then link them (via an update).
   See `migrations/001-based-data` for an example.
2. Excel import (`Action_Seed_Excel`) - any Excel file can be provided here. The file will be parsed into `SeedData` with a to be provided decoder.
   See `migrations/002-excel-example` for an example.
3. Custom logic (`Action_Seed_Custom`) - any custom logic can be provided here. This is useful for more complex migrations that cannot be handled by the other two types.
   See `migrations/003-custom-logic` for an example.

### Add new actions/mutations

Actions are typically scoped to a specific update service, i.e. should be defined in the corresponding update service file.

To add a new action, you typically need to create/extend the following in the service file:

- Define the action type, e.g. `export type Action_Post_Create = { scope: 'post'; type: 'create'; payload: PostCreateDTO }`
- For convenience for the dispatching routes, define a corresponding `createAction` function, e.g. `export const createActionPostCreate = (payload: PostCreateDTO): Action_Post_Create => ({ scope: 'post', type: 'create', payload })`
- Add the action to the service's `Action` type, e.g. `export type Action_Post = Action_Post_Create | ...`
- Add the result type to the service's `ActionResult` type, e.g. `export type ActionResult_Post = {  create: Post, ... }`
- Add the action handler as a method to the service, e.g. `public async create({ item, execution }: { item: PostCreateDTO; execution: IActionExecution }): Promise<Post> { ... }`
  Ensure that the method returns the same type as defined in the `ActionResult` type!
  Note that the `execution` parameter is required and will be provided by the dispatcher (see below). This execution context is required for any CRUD changes of any model's repository.
- Extend the `dispatch` method to handle the new action, e.g. `case 'post': return this.dispatchPost(action, execution)`
