language-learning-app/api/docs/architecture.md

105 lines
4.9 KiB
Markdown
Raw Normal View History

# Language Learning App API Architecture
This is a HTTP API, written in Python, using the Fastapi framework.
The code should be organised using both Domain Driven Design and Hexagonal Architecture principles. Domain driven design is present when method names are human readable, and translate to what's actually happening or the process it's trying to model.
For example, consider the following:
```py
flashcard_service.create('bonjour', 'hello', 'en', 'fr', user_id)
flashcard_service.record_event(flashcard_id, 'seen', user_id)
```
vs.
```py
flascard_service.create_flashcard_in_language_pair_for_user(
['bonjour', 'hello'],
['fr', 'en'],
user_id
)
flashcard_service.record_card_seen_by_user(flashcard_id, user_id, now)
```
The latter is closer to how the code should read, as a set of more human-readable statements.
Although the structure is outlined below, pragmatism wins over zealous adherence.
## Domain
The `domain` directory contains the core logic of my application, not related to specific implementation details.
This is where all of the logic around the actual language learning lives.
### Models
In `app/domain/models` contains the core Domain Entities, i.e. classes that represent objects for core domain processes.
Where possible, the codebase adopts an object-oriented approach. The models therefore present tangible entities. in the system.
### Services
The `app/domain/services` directory contains modules that encapsulate the "orchestration" or "choreography" of other components of the system to achieve complex, domain actions.
For example:
- `TextGenerationService` details the step-by-step process of how a series of text is synthesised, audio is generated, parts of speech tagged, timed transcripts generated, etc. which is then used by the learner for language learning.
### Routers
This is where definitions of the HTTP endpoints lives.
Logic that lives here is called 'controller logic' (after the Rails convention), and it should be responsible for processing an incoming request and formatting an outgoing response.
Business logic should be delegated, as much as possible, to the relevant services and utilities.
There are two kinds of endpoints: `/api` and `/bff`
#### API Routers
Routes which start with `/api` often signify _actions_ the user wants to take, e.g. registering or logging in; creating a Flashcard Event; retrieving the next batch of cards.
These are resource-oriented typical RESTful endpoints. GET for retrieving, POST for creating, PUT for updating, DELETE for deletion.
#### BFF Routers
BFF stands for "backend for frontend". BFF routes are those used on specific screens in the Web UI an allow the loading of varies types of data from various sources. Because they are not "pure" RESTful endpoints, which are resource-driven, they acknowledge that they are tied to a specific screen in the UI.
BFF endpoints are only ever GET, never POST.
Even if two screens _seem_ identical, they will get separate BFFs, to allow separate evolution.
The BFF route will, as much as possible, be the route on the UI prefaced with `/bff`. E.g. the `/account` screen on the UI would be powered by the `/bff/account` endpoint.
## Outbound
The `app/outbound` directory contains modules that allow this project to communicate with external systems.
### Repositories and persistence
This project uses SQLAlchemy for persistence and a postgresql database for storage. This is the same in local, deployed, and test environments.
Notably the `app/outbound/postgres` directory contains two modules:
- `entities` contains definitions of tables in the database. These are how the Domain Entities are serialised, but this code specifically is used for serialisation to, and de-serialisation from, persisted data and into Domain Entities from the `models` module.
- `repositories` module contains classes which manage transactions (e.g. CRUD operations) with the psotgresql database, managed through sqlite. Code in the repositories module are what allow Models to be persisted via Entities, allowing all other code to be free of implementation details.
### API Clients
Other modules in the wider `app/outbound` directory are about communicating with specific third-party APIs, notably through HTTP APIs authenticated with an authentication token.
These modules are modelled as Classes.
Example Api Clients in their own modules are:
- `AnthropicClient` to communicate with Anthorpic's LLM, i.e. Claude, to generate text and synthesis.
- `GeminiClient` to communicate with Google's Gemini for text-to-speech generation
- `DeepgramClient` for timestamped speech-to-text transcription
## Deploymnet
The application has not been deployed yet, but local development should mimic the deployed environment as much as possible.
It will be deployed on a VPS using containerisation technologies (docker, podman). At the root of the projec there is a `docker-compose.yaml` file which will describe each dependency (e.g. database, queue, storage).