@rashidee/co2 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +25449 -0
- package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
- package/drizzle/0001_wise_wolf_cub.sql +13 -0
- package/drizzle/0002_secret_spyke.sql +16 -0
- package/drizzle/0003_workspace_config_singleton.sql +16 -0
- package/drizzle/0004_terminal_claim_v120.sql +11 -0
- package/drizzle/0005_brainstorm_v124.sql +10 -0
- package/drizzle/0006_project_v140.sql +6 -0
- package/drizzle/0007_prd_wizard_v124.sql +14 -0
- package/drizzle/0008_workflow_artifact_v126.sql +24 -0
- package/drizzle/0009_project_import_v150.sql +7 -0
- package/drizzle/0010_quality_v151.sql +3 -0
- package/drizzle/meta/0000_snapshot.json +1335 -0
- package/drizzle/meta/0001_snapshot.json +1416 -0
- package/drizzle/meta/0002_snapshot.json +1510 -0
- package/drizzle/meta/_journal.json +83 -0
- package/package.json +41 -0
- package/plugin/.claude-plugin/marketplace.json +36 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/README.md +625 -0
- package/plugin/SKILLS.md +350 -0
- package/plugin/co2.svg +5 -0
- package/plugin/commands/brainstorm-loop.md +129 -0
- package/plugin/skills/conductor-defect/SKILL.md +898 -0
- package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
- package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
- package/plugin/skills/depgen-k8s/SKILL.md +468 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
- package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
- package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
- package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
- package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
- package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
- package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
- package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
- package/plugin/skills/modelgen-relational/SKILL.md +707 -0
- package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
- package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
- package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
- package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
- package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
- package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
- package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
- package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
- package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
- package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
- package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
- package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
- package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
- package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
- package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
- package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
- package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
- package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
- package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
- package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
- package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
- package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
- package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
- package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
- package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
- package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
- package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
- package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
- package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
- package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
- package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
- package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
- package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
- package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
- package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
- package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
- package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
- package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
- package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
- package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
- package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
- package/plugin/skills/testgen-functional/SKILL.md +1381 -0
- package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
- package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
- package/plugin/skills/util-projectinit/SKILL.md +307 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
- package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
- package/plugin/skills/util-projectsync/SKILL.md +501 -0
- package/plugin/skills/util-updprd/SKILL.md +324 -0
- package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
- package/plugin/skills/util-ustagger/SKILL.md +154 -0
- package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
- package/static/assets/arc-DRKw7sjs.js +1 -0
- package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
- package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
- package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
- package/static/assets/channel-C2nVbuwv.js +1 -0
- package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
- package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
- package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
- package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
- package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
- package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
- package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
- package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
- package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
- package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
- package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
- package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
- package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
- package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
- package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
- package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
- package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
- package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
- package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
- package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
- package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
- package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
- package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
- package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
- package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
- package/static/assets/graph-C9eacEi8.js +1 -0
- package/static/assets/index-LZSQkLE2.css +41 -0
- package/static/assets/index-y7XDSgMG.js +495 -0
- package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
- package/static/assets/init-Gi6I4Gst.js +1 -0
- package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
- package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
- package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
- package/static/assets/katex-C5jXJg4s.js +257 -0
- package/static/assets/layout-DEXfKzaS.js +1 -0
- package/static/assets/linear-CLO--ryk.js +1 -0
- package/static/assets/map-Czzmt4hB.js +1 -0
- package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
- package/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
- package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
- package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
- package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
- package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
- package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
- package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
- package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
- package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
- package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
- package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
- package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
- package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
- package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
- package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
- package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
- package/static/index.html +24 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# Spring Modulith Patterns — Module Structure & Event Architecture
|
|
2
|
+
|
|
3
|
+
This reference describes the Spring Modulith conventions used in the spec. Include this
|
|
4
|
+
content in Sections 4 and 8 of the generated specification.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Module Boundary Rules
|
|
9
|
+
|
|
10
|
+
Spring Modulith enforces architectural boundaries by treating each top-level package
|
|
11
|
+
under the base package as an independent application module. This spec uses the strict
|
|
12
|
+
encapsulation model: public API at the module root, all implementation in `internal/`.
|
|
13
|
+
|
|
14
|
+
### What constitutes a module
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
com.example.app/
|
|
18
|
+
├── config/ ← NOT a module (infrastructure config)
|
|
19
|
+
├── shared/ ← NOT a module (shared kernel, accessible by all)
|
|
20
|
+
├── order/ ← MODULE: order bounded context
|
|
21
|
+
├── inventory/ ← MODULE: inventory bounded context
|
|
22
|
+
├── scheduling/ ← MODULE: scheduling bounded context
|
|
23
|
+
└── Application.java
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Spring Modulith scans `Application.java`'s package and treats each direct sub-package
|
|
27
|
+
as a module, EXCEPT for packages explicitly designated as shared infrastructure.
|
|
28
|
+
|
|
29
|
+
### Visibility rules — Public API vs Internal Implementation
|
|
30
|
+
|
|
31
|
+
Each module has a strict two-layer structure:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
com.example.app.order/ ← PUBLIC API (visible to other modules)
|
|
35
|
+
├── OrderService.java ← Service interface
|
|
36
|
+
├── OrderDTO.java ← Return type for inter-module communication
|
|
37
|
+
├── OrderException.java ← Module-specific exception
|
|
38
|
+
├── OrderCreatedEvent.java ← Domain event (visible to listeners)
|
|
39
|
+
│
|
|
40
|
+
└── internal/ ← INTERNAL (hidden from other modules)
|
|
41
|
+
├── OrderServiceImpl.java ← Implements OrderService
|
|
42
|
+
├── OrderRepository.java ← MongoRepository — data access
|
|
43
|
+
├── OrderDocument.java ← MongoDB document — data model
|
|
44
|
+
├── OrderMapper.java ← MapStruct mapper
|
|
45
|
+
└── web/
|
|
46
|
+
└── OrderController.java ← REST controller (inbound adapter)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Public API (module root)** — only these types are visible to other modules:
|
|
50
|
+
- `OrderService` — Interface. The sole programmatic entry point for other modules.
|
|
51
|
+
Other modules inject this interface; Spring wires the internal `OrderServiceImpl`.
|
|
52
|
+
- `OrderDTO` — The data contract. When `OrderService` returns data, it returns this
|
|
53
|
+
DTO, never the internal `OrderDocument`. This prevents leaking persistence details.
|
|
54
|
+
- `OrderException` — Callers can catch module-specific failures.
|
|
55
|
+
- `OrderCreatedEvent` — Domain events must be in the root package so listeners in
|
|
56
|
+
other modules can reference the event type.
|
|
57
|
+
|
|
58
|
+
**Internal implementation (`internal/`)** — invisible to other modules:
|
|
59
|
+
- `OrderServiceImpl` — Contains all business logic, repository calls, event publishing.
|
|
60
|
+
Spring Modulith treats everything in `internal/` as module-private.
|
|
61
|
+
- `OrderRepository` — Data access is an implementation detail. Other modules never
|
|
62
|
+
query another module's database directly.
|
|
63
|
+
- `OrderDocument` — The MongoDB document model is internal. It may have fields,
|
|
64
|
+
embedded objects, or annotations that are irrelevant to consumers.
|
|
65
|
+
- `OrderMapper` — DTO ↔ Document conversion is an internal concern.
|
|
66
|
+
- `web/OrderController` — The REST controller is an **inbound adapter**. It accepts
|
|
67
|
+
HTTP requests and delegates to the service interface. It is NOT part of the
|
|
68
|
+
inter-module API — other modules never call controllers. Placing it in `internal/web/`
|
|
69
|
+
makes this architectural intent explicit and enforceable.
|
|
70
|
+
- `OrderEventConsumer` *(if Messaging = yes)* — The `@RabbitListener` is also an
|
|
71
|
+
**inbound adapter** (MQ instead of HTTP) and follows the same rule: it lives in
|
|
72
|
+
`internal/`, is package-private, and delegates to the module's service interface.
|
|
73
|
+
It must NEVER be placed under `shared/messaging/` — see `messaging-patterns.md`
|
|
74
|
+
for the full convention.
|
|
75
|
+
|
|
76
|
+
### Why the controller is internal
|
|
77
|
+
|
|
78
|
+
This is a deliberate architectural choice. In hexagonal / ports-and-adapters terms:
|
|
79
|
+
- The **port** is `OrderService` (the interface) — this is the module's public API
|
|
80
|
+
- The **adapter** is `OrderController` — it adapts HTTP to the service interface
|
|
81
|
+
|
|
82
|
+
Other modules interact through the port (service interface), not the adapter (controller).
|
|
83
|
+
If tomorrow you add a gRPC adapter or a message consumer, they would also be internal
|
|
84
|
+
adapters delegating to the same public service interface. Keeping adapters internal
|
|
85
|
+
enforces this separation.
|
|
86
|
+
|
|
87
|
+
### Inter-module dependency direction
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
OrderController (internal) → OrderService (public interface)
|
|
91
|
+
↑
|
|
92
|
+
InventoryService (another module) → OrderService (public interface)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Both the internal controller and external modules depend on the same public interface.
|
|
96
|
+
The `OrderServiceImpl` is wired by Spring but never directly referenced.
|
|
97
|
+
|
|
98
|
+
### What modules must NOT do
|
|
99
|
+
|
|
100
|
+
- Never inject a ServiceImpl from another module — use the Service interface
|
|
101
|
+
- Never reference another module's `internal` package (enforced by Spring Modulith)
|
|
102
|
+
- Never reference another module's Repository or Document
|
|
103
|
+
- Never create circular dependencies between modules
|
|
104
|
+
- Never bypass the service interface by calling another module's controller
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Inter-Module Communication via Events
|
|
109
|
+
|
|
110
|
+
When one module needs to notify others about a domain event (e.g., an order was created
|
|
111
|
+
and inventory needs to adjust), use Spring Modulith application events.
|
|
112
|
+
|
|
113
|
+
### Event Class Convention
|
|
114
|
+
|
|
115
|
+
Events are Java records, immutable, and carry only the data the listener needs.
|
|
116
|
+
|
|
117
|
+
Naming: `<AggregateRoot><PastTenseVerb>Event`
|
|
118
|
+
|
|
119
|
+
```java
|
|
120
|
+
// In com.example.app.order (module root — visible to other modules)
|
|
121
|
+
public record OrderCreatedEvent(
|
|
122
|
+
String orderId,
|
|
123
|
+
String customerId,
|
|
124
|
+
Instant occurredAt
|
|
125
|
+
) {}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Why records: they're immutable, generate equals/hashCode, and make the event's contract
|
|
129
|
+
explicit. Using `Instant occurredAt` establishes when the event happened.
|
|
130
|
+
|
|
131
|
+
### Publishing Events
|
|
132
|
+
|
|
133
|
+
The service implementation (internal) publishes events using Spring's `ApplicationEventPublisher`:
|
|
134
|
+
|
|
135
|
+
```java
|
|
136
|
+
// In com.example.app.order.internal (INTERNAL — hidden from other modules)
|
|
137
|
+
@Service
|
|
138
|
+
@RequiredArgsConstructor
|
|
139
|
+
@Slf4j
|
|
140
|
+
class OrderServiceImpl implements OrderService {
|
|
141
|
+
private final OrderRepository repository;
|
|
142
|
+
private final OrderMapper mapper;
|
|
143
|
+
private final ApplicationEventPublisher eventPublisher;
|
|
144
|
+
|
|
145
|
+
@Override
|
|
146
|
+
@Transactional
|
|
147
|
+
public OrderDTO create(CreateOrderRequest request) {
|
|
148
|
+
OrderDocument saved = repository.save(mapper.toDocument(request));
|
|
149
|
+
|
|
150
|
+
eventPublisher.publishEvent(new OrderCreatedEvent(
|
|
151
|
+
saved.getId(),
|
|
152
|
+
saved.getCustomerId(),
|
|
153
|
+
Instant.now()
|
|
154
|
+
));
|
|
155
|
+
|
|
156
|
+
log.info("Order created: {}", saved.getId());
|
|
157
|
+
return mapper.toDTO(saved);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Note that the class has package-private visibility (no `public` keyword). Spring still
|
|
163
|
+
discovers it via component scanning, but other modules cannot reference it directly.
|
|
164
|
+
They depend on the `OrderService` interface instead.
|
|
165
|
+
|
|
166
|
+
Events are published within the transaction boundary. If the transaction rolls back,
|
|
167
|
+
the event is not delivered.
|
|
168
|
+
|
|
169
|
+
### Listening to Events
|
|
170
|
+
|
|
171
|
+
Other modules listen using `@ApplicationModuleListener` in their internal implementation:
|
|
172
|
+
|
|
173
|
+
```java
|
|
174
|
+
// In com.example.app.inventory.internal (INTERNAL)
|
|
175
|
+
@Service
|
|
176
|
+
@RequiredArgsConstructor
|
|
177
|
+
@Slf4j
|
|
178
|
+
class InventoryServiceImpl implements InventoryService {
|
|
179
|
+
|
|
180
|
+
@ApplicationModuleListener
|
|
181
|
+
void on(OrderCreatedEvent event) {
|
|
182
|
+
log.info("Adjusting inventory for order: {}", event.orderId());
|
|
183
|
+
// Business logic to reserve inventory
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The listener references `OrderCreatedEvent` which is in the `order` module's root
|
|
189
|
+
package (public API), so this cross-module reference is valid. The implementation
|
|
190
|
+
class itself is internal to the `inventory` module.
|
|
191
|
+
|
|
192
|
+
`@ApplicationModuleListener` is a composed annotation that includes `@Async` and
|
|
193
|
+
`@TransactionalEventListener`. This means:
|
|
194
|
+
- The listener runs in a separate transaction
|
|
195
|
+
- It executes after the publishing transaction commits
|
|
196
|
+
- Failures in the listener do not roll back the publisher
|
|
197
|
+
|
|
198
|
+
### Event Publication Registry
|
|
199
|
+
|
|
200
|
+
Spring Modulith's event publication registry (backed by MongoDB via
|
|
201
|
+
`spring-modulith-starter-mongodb`) persists events. If a listener fails, the event
|
|
202
|
+
is marked as incomplete and can be retried.
|
|
203
|
+
|
|
204
|
+
Configuration in `application.yml`:
|
|
205
|
+
```yaml
|
|
206
|
+
spring:
|
|
207
|
+
modulith:
|
|
208
|
+
events:
|
|
209
|
+
republish-outstanding-events-on-restart: true
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This ensures at-least-once delivery semantics for application events.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Module Verification
|
|
217
|
+
|
|
218
|
+
Spring Modulith provides a test that verifies all module boundary rules are respected:
|
|
219
|
+
|
|
220
|
+
```java
|
|
221
|
+
@Test
|
|
222
|
+
void verifyModularity() {
|
|
223
|
+
ApplicationModules.of(Application.class).verify();
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This test catches:
|
|
228
|
+
- Direct dependencies between modules that bypass events
|
|
229
|
+
- Circular module dependencies
|
|
230
|
+
- References to another module's internal types
|
|
231
|
+
|
|
232
|
+
Include this test in the Testing Strategy section of the spec. It should run as part
|
|
233
|
+
of the build to enforce architectural integrity continuously.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Module Documentation
|
|
238
|
+
|
|
239
|
+
Spring Modulith can generate architecture documentation from the module structure:
|
|
240
|
+
|
|
241
|
+
```java
|
|
242
|
+
@Test
|
|
243
|
+
void createModuleDocumentation() {
|
|
244
|
+
ApplicationModules modules = ApplicationModules.of(Application.class);
|
|
245
|
+
new Documenter(modules).writeDocumentation();
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
This produces PlantUML diagrams and Asciidoc documentation of module interactions.
|
|
250
|
+
Mention this as an optional capability in the spec.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Web Application Module Adaptations
|
|
255
|
+
|
|
256
|
+
For server-rendered web applications, the module structure is adapted to include `page`
|
|
257
|
+
and `fragment` subpackages within `internal/`. The controller is a `@Controller` (not
|
|
258
|
+
`@RestController`) that returns JTE view names.
|
|
259
|
+
|
|
260
|
+
### Web Module Structure
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
com.example.app.order/ ← PUBLIC API (visible to other modules)
|
|
264
|
+
├── OrderService.java ← Service interface
|
|
265
|
+
├── OrderDTO.java ← Return type for inter-module communication
|
|
266
|
+
├── OrderException.java ← Module-specific exception
|
|
267
|
+
│
|
|
268
|
+
└── internal/ ← INTERNAL (hidden from other modules)
|
|
269
|
+
├── OrderServiceImpl.java ← Implements OrderService
|
|
270
|
+
├── OrderRepository.java ← MongoRepository — data access
|
|
271
|
+
├── OrderDocument.java ← MongoDB document — data model
|
|
272
|
+
├── OrderMapper.java ← MapStruct mapper
|
|
273
|
+
├── page/ ← Page controllers (full HTML pages)
|
|
274
|
+
│ ├── OrderPageController.java ← @Controller returning JTE view names
|
|
275
|
+
│ ├── OrderListView.java ← View model for list page
|
|
276
|
+
│ └── OrderDetailView.java ← View model for detail page
|
|
277
|
+
└── fragment/ ← Fragment controllers (htmx partials)
|
|
278
|
+
└── OrderFragmentController.java ← Returns HTML fragments for htmx
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Key Differences from API Modules
|
|
282
|
+
|
|
283
|
+
1. **No REST controllers**: Web modules use `@Controller` instead of `@RestController`.
|
|
284
|
+
Controllers return JTE template paths, not JSON.
|
|
285
|
+
|
|
286
|
+
2. **Direct data ownership**: Each module owns its MongoDB documents and repositories.
|
|
287
|
+
The service layer queries MongoDB directly via Spring Data `MongoRepository`.
|
|
288
|
+
|
|
289
|
+
3. **View models**: Each page has a dedicated view model class that encapsulates all
|
|
290
|
+
data needed by the JTE template. View models are internal to the module.
|
|
291
|
+
|
|
292
|
+
4. **Page vs Fragment separation**: `page/` subpackage holds controllers for full page
|
|
293
|
+
renders; `fragment/` holds controllers that return partial HTML for htmx requests.
|
|
294
|
+
|
|
295
|
+
5. **Mixins**: Controllers implement `PaginationAware`, `ThemeAware`, and `AuthAware`
|
|
296
|
+
interfaces to extract pagination, theme, and user info from the request.
|
|
297
|
+
|
|
298
|
+
### Web Page Controller Pattern
|
|
299
|
+
|
|
300
|
+
```java
|
|
301
|
+
@Controller
|
|
302
|
+
@RequestMapping("/orders")
|
|
303
|
+
@RequiredArgsConstructor
|
|
304
|
+
class OrderPageController implements PaginationAware, ThemeAware, AuthAware {
|
|
305
|
+
|
|
306
|
+
private final OrderService service;
|
|
307
|
+
private final MainLayout layout;
|
|
308
|
+
|
|
309
|
+
@GetMapping
|
|
310
|
+
String list(Model model, HttpServletRequest req) {
|
|
311
|
+
var pageRequest = pagination(req, 20, 100);
|
|
312
|
+
var result = service.list(pageRequest);
|
|
313
|
+
var paginationVm = result.toPagination("/orders");
|
|
314
|
+
var view = new OrderListView(result.data(), paginationVm, theme(req), currentUser(req));
|
|
315
|
+
model.addAttribute("layout", layout.defaultPage("Orders", req));
|
|
316
|
+
model.addAttribute("view", view);
|
|
317
|
+
return "com/example/app/order/page/OrderListPage";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@GetMapping("/{id}")
|
|
321
|
+
String detail(@PathVariable String id, Model model, HttpServletRequest req) {
|
|
322
|
+
var dto = service.get(id);
|
|
323
|
+
var view = new OrderDetailView(dto, theme(req), currentUser(req));
|
|
324
|
+
model.addAttribute("layout", layout.defaultPage(dto.getFieldOne(), req));
|
|
325
|
+
model.addAttribute("view", view);
|
|
326
|
+
return "com/example/app/order/page/OrderDetailPage";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Web Fragment Controller Pattern
|
|
332
|
+
|
|
333
|
+
```java
|
|
334
|
+
@Controller
|
|
335
|
+
@RequestMapping("/orders/fragments")
|
|
336
|
+
@RequiredArgsConstructor
|
|
337
|
+
class OrderFragmentController implements PaginationAware, AuthAware {
|
|
338
|
+
|
|
339
|
+
private final OrderService service;
|
|
340
|
+
|
|
341
|
+
@GetMapping("/row/{id}")
|
|
342
|
+
String row(@PathVariable String id, Model model) {
|
|
343
|
+
var dto = service.get(id);
|
|
344
|
+
model.addAttribute("item", dto);
|
|
345
|
+
return "com/example/app/order/fragment/OrderRowFragment";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@DeleteMapping("/{id}")
|
|
349
|
+
ResponseEntity<String> delete(@PathVariable String id) {
|
|
350
|
+
service.delete(id);
|
|
351
|
+
return ResponseEntity.ok("");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|