Project structure

This project and documentation are work in progress

Take a look at the NTA example project, and open the src folder. You'll see a structure that looks like this:

- lib
- adapters
- bindings
- app (or apps)

Each represents a key component of NTA:

  • The lib contains the 'core' domain code, and describes what entities exist in the system and what a user can do with them

  • The adapters provide functions to help the lib achieve side effects in different configurations

  • The bindings take a lib plus adapters and make it responsive to messages from the outside world

  • Each folder in apps combines the lib with adapters and a binding to make something runnable

Let's examine each of these folders in more detail.

The lib folder

The library contains all our domain code, core to the model of whatever system we're building. It has a structure like this:

- entities
- operations
- events
- errors
- context
- index.ts

Entities are types describing the objects or 'things' that exist within your domain model, such as users, orders, invoices and payments.

Operations are functions that do things to and with entities, using a context parameter to provide functions that do side effects

Events are types representing any kinds of events dispatched by your system. This folder is optional.

Errors are where you define any custom errors thrown by operations. You'll use these types a lot once you start having to decide what to do after an operation throws an exception.

Context is where we define the type of the context used by operations.

Finally, the index exports a function that can create a usable instance of the library.

The adapters

Adapters give the operations 'context' with which they can achieve side effects. We'll discuss how they actually do this a little later, but for now: adapters are functions that are called at runtime to give an app a particular capability, like speaking to a Postgres database or API backend, or sending events to other services.

We discuss adapters at more length in Adapters uncovered.

The bindings

Once a library has been combined with adapters, it is ready to be passed to a binding. These functions attach the library to events coming from the outside world, such as HTTP endpoints or CLI commands.

The distinction between adapters and bindings is as follows: bindings bridge the gap between the app and the user, whereas adatpers bridge the gap between the app and other systems.

The app (or apps) folder

Apps are modules that combine the library, some adapters, and a binding to create a usable program. Most projects will start with one app, but this can be extended.

Where do tests go?

Some people like their tests to sit alongside source code, others prefer to separate them out. Personally I do the latter, with my test structure mirroring the src structure, but what really matters is that you're consistent, so people can easily find them.

Last updated