Article · Engineering
Building Amitable: architecture and engineering principles
The engineering ideas behind Amitable: explicit boundaries, typed data, repository APIs, and observable workflows.
Read in another language: RUBuilding Amitable means working on both a product model and the infrastructure that keeps that model understandable. The repository currently demonstrates a NestJS GraphQL API, PostgreSQL persistence, Socket.IO updates, and a static Astro site around that core.
Keep boundaries visible
Workspaces are the primary boundary for records, memberships, permissions, and realtime updates. A collection belongs to a workspace, and references between fields are checked against that same boundary. This keeps a useful product rule close to the code that enforces it: connected data should not accidentally become cross-workspace data.
The API is split into feature modules. Resolvers describe the GraphQL surface, services hold business operations, and repositories make persistence choices explicit. That structure leaves room to test a workflow without making every test boot the whole application.
Use typed data where the product has meaning
Document records have system metadata in typed PostgreSQL columns. User-defined values live in JSONB because their shape is configured by the collection. The two layers are deliberately separate:
type DocumentRecord = {
id: string
workspaceId: string
documentCollectionId: string
values: Record<string, unknown>
version: number
}
GraphQL validation prevents clients from writing system metadata as business values. Field definitions describe whether a value is a string, number, boolean, or reference, and reference rules are validated before persistence.
Make changes observable
Record changes are versioned and distributed through the authenticated realtime namespace. A client can catch up from a known cursor instead of treating every connection as a blank slate. This matters for a product where several people may edit the same collection while they are also importing, filtering, or reviewing records.
The same principle applies to background work: a job should have a visible state, an idempotent identity, and a failure that can be investigated without guessing what happened.
Prefer boring, explicit operations
The project favors repository and entity-manager APIs for persistence. A feature should make its transaction, authorization boundary, and external side-effects easy to locate. When a workflow crosses PostgreSQL, realtime delivery, or an integration, explicit steps are easier to retry and verify than a hidden chain of callbacks.
These principles are still being tested in the product. The architecture is not a promise that every decision is finished; it is a way to keep the next decision local, typed, and reversible.
