A concept is an independent, reusable behavioral unit. It owns its own state, exposes actions for mutation, and provides queries for inspection. A concept is like a micro-service that runs in-process — it has a clear API, internal state, and no knowledge of other concepts.
Every concept has:
[Entry], [User])requires and effects_) that return arrays of rowsconcept Routing [Entry]
purpose
assign stable, unambiguous public routes to entries
principle
after a routing scheme is configured and an entry is assigned a path,
the entry has one derived public route; assigning another entry that
would use the same route is rejected
state
a set of RoutedEntries with
an Entry
a filePath String
a route String
actions
configure (stripPrefix?, indexName?): ()
derive (entry, filePath): (entry, route)
remove (entry): (entry)
queries
_getRoute (entry): (route)
_getByRoute (route): (entry)
Each concept is a TypeScript class. State is stored in Map instances. Actions are async methods that return typed result objects. Error cases return { error: string } — they never throw.
export default class RoutingConcept {
private entries = new Map<Entry, EntryDoc>();
async derive({ entry, filePath }: {
entry: ID; filePath: string;
}): Promise<{ entry: ID; route: string } | { error: string }> {
// compute route...
// check for collisions...
this.entries.set(entry, { _id: entry, filePath, route });
return { entry, route };
}
}
A concept must never import another concept. It treats external identities (like Entry) as opaque — it never assumes properties about them. This is what makes concepts truly reusable across different applications.
State components that can evolve independently are a warning that multiple concerns have been combined. For example, a Filing concept that manages file discovery, content mutation, output configuration, AND publication is doing too much. Split it into Discovering, Reading, and Publishing.