Skip to content

Commit 7be523a

Browse files
Add modular reference example
1 parent b06c677 commit 7be523a

1 file changed

Lines changed: 141 additions & 0 deletions

File tree

docs/examples/MODULAR_REFERENCE.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Modular Reference Example
2+
3+
This example shows how to use the template's architecture without assuming a specific product, framework, or runtime.
4+
5+
The goal is not to provide production code. The goal is to show the dependency direction:
6+
7+
```text
8+
plugins -> providers -> core
9+
```
10+
11+
`core/` owns stable behavior. `providers/` adapt outside systems. `plugins/` compose optional capabilities.
12+
13+
## Example scenario
14+
15+
Imagine a project needs to normalize an input item, save it somewhere, and optionally expose that behavior as an extension.
16+
17+
This can be modeled without committing to a database, API, queue, CLI, or web framework.
18+
19+
## 1) Core behavior
20+
21+
`core/` defines the stable rules and contracts.
22+
23+
```text
24+
core/item-normalizer.pseudo
25+
26+
function normalizeItem(input): NormalizedItem
27+
require input.name is not empty
28+
29+
return {
30+
name: trim(input.name),
31+
slug: lowercase(replaceSpaces(input.name, "-"))
32+
}
33+
```
34+
35+
Why it belongs in `core/`:
36+
37+
- it does not know where data came from
38+
- it does not know where data will be stored
39+
- it is easy to test in isolation
40+
- it can be reused by many interfaces
41+
42+
## 2) Provider adapter
43+
44+
`providers/` connects the core behavior to an outside system.
45+
46+
```text
47+
providers/item-store.pseudo
48+
49+
interface ItemStore
50+
save(item): SaveResult
51+
52+
class FileItemStore implements ItemStore
53+
save(item)
54+
write item to configured file path
55+
```
56+
57+
Why it belongs in `providers/`:
58+
59+
- file storage is an implementation detail
60+
- it can later be swapped for an API, database, queue, or memory store
61+
- `core/` does not need to change when storage changes
62+
63+
## 3) Optional plugin
64+
65+
`plugins/` wires optional behavior together.
66+
67+
```text
68+
plugins/save-normalized-item.pseudo
69+
70+
function createSaveNormalizedItemPlugin(itemStore)
71+
return function run(input)
72+
item = normalizeItem(input)
73+
return itemStore.save(item)
74+
```
75+
76+
Why it belongs in `plugins/`:
77+
78+
- it composes core behavior with a provider
79+
- it can be enabled, replaced, or removed
80+
- it keeps optional workflow logic outside the core layer
81+
82+
## 4) Configuration
83+
84+
`config/` documents runtime choices without hardcoding machine-specific values.
85+
86+
```text
87+
config/item-store.example
88+
89+
ITEM_STORE_KIND=file
90+
ITEM_STORE_PATH=./data/items.json
91+
```
92+
93+
Keep real secrets, local-only paths, and machine-specific values out of committed files.
94+
95+
## 5) Tests
96+
97+
`tests/` should start closest to the core behavior.
98+
99+
```text
100+
tests/unit/item-normalizer.test.pseudo
101+
102+
input: { name: " Example Item " }
103+
output: { name: "Example Item", slug: "example-item" }
104+
```
105+
106+
Suggested test layers:
107+
108+
- unit tests for `core/` behavior
109+
- integration tests for `providers/`
110+
- plugin tests for composition boundaries
111+
112+
## Dependency rule
113+
114+
The important rule is direction:
115+
116+
```text
117+
core: knows only stable rules
118+
providers: know outside systems and implement contracts
119+
plugins: compose optional workflows
120+
```
121+
122+
Avoid this:
123+
124+
```text
125+
core -> providers
126+
core -> plugins
127+
```
128+
129+
That direction makes the template harder to reuse and harder to test.
130+
131+
## How to adapt this example
132+
133+
When starting a real project:
134+
135+
1. Put pure behavior and contracts in `core/`.
136+
2. Put external systems in `providers/`.
137+
3. Put optional feature wiring in `plugins/`.
138+
4. Put safe examples and schemas in `config/`.
139+
5. Put validation coverage in `tests/`.
140+
141+
Keep the seams boring. Boring seams make future changes cheap.

0 commit comments

Comments
 (0)