@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,461 @@
|
|
|
1
|
+
# Laravel Modules Patterns — Module Structure & Event Architecture
|
|
2
|
+
|
|
3
|
+
This reference describes the nwidart/laravel-modules conventions used in the spec. Include
|
|
4
|
+
this content in Sections 5 and 19 of the generated specification.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Module Boundary Rules
|
|
9
|
+
|
|
10
|
+
nwidart/laravel-modules enforces modular boundaries by treating each module as an
|
|
11
|
+
independent package with its own namespace, routes, views, migrations, and service
|
|
12
|
+
providers. This spec uses a strict encapsulation model: public API via contracts and
|
|
13
|
+
DTOs, all implementation internal to the module.
|
|
14
|
+
|
|
15
|
+
### What constitutes a module
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
app/
|
|
19
|
+
├── Http/Middleware/ ← NOT a module (shared middleware)
|
|
20
|
+
├── Services/ ← NOT a module (shared services)
|
|
21
|
+
├── Models/ ← NOT a module (shared base models like User)
|
|
22
|
+
├── Traits/ ← NOT a module (shared traits)
|
|
23
|
+
├── Constants/ ← NOT a module (shared constants)
|
|
24
|
+
├── Exceptions/ ← NOT a module (shared exceptions)
|
|
25
|
+
└── Providers/ ← NOT a module (shared service providers)
|
|
26
|
+
|
|
27
|
+
Modules/
|
|
28
|
+
├── Order/ ← MODULE: Order bounded context
|
|
29
|
+
├── Inventory/ ← MODULE: Inventory bounded context
|
|
30
|
+
├── Scheduling/ ← MODULE: Scheduling bounded context
|
|
31
|
+
└── ...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Each module is generated via `php artisan module:make <ModuleName>` and follows the
|
|
35
|
+
nwidart/laravel-modules directory convention.
|
|
36
|
+
|
|
37
|
+
### Visibility rules — Public API vs Internal Implementation
|
|
38
|
+
|
|
39
|
+
Each module has a strict two-layer structure:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Modules/Order/ ← MODULE ROOT
|
|
43
|
+
├── Contracts/ ← PUBLIC API (visible to other modules)
|
|
44
|
+
│ └── OrderServiceInterface.php ← Service interface
|
|
45
|
+
├── DTOs/ ← PUBLIC API
|
|
46
|
+
│ └── OrderData.php ← spatie/laravel-data DTO
|
|
47
|
+
├── Events/ ← PUBLIC API
|
|
48
|
+
│ └── OrderCreatedEvent.php ← Domain event
|
|
49
|
+
├── Exceptions/ ← PUBLIC API
|
|
50
|
+
│ └── OrderException.php ← Module-specific exception
|
|
51
|
+
│
|
|
52
|
+
├── Services/ ← INTERNAL
|
|
53
|
+
│ └── OrderService.php ← Implements OrderServiceInterface
|
|
54
|
+
├── Models/ ← INTERNAL
|
|
55
|
+
│ └── Order.php ← Eloquent model
|
|
56
|
+
├── Repositories/ ← INTERNAL (optional)
|
|
57
|
+
│ └── OrderRepository.php ← Query abstraction
|
|
58
|
+
├── Http/ ← INTERNAL
|
|
59
|
+
│ ├── Controllers/
|
|
60
|
+
│ │ ├── OrderPageController.php ← Page controller (full HTML)
|
|
61
|
+
│ │ └── OrderFragmentController.php ← Fragment controller (htmx)
|
|
62
|
+
│ └── Requests/
|
|
63
|
+
│ ├── StoreOrderRequest.php ← Form validation
|
|
64
|
+
│ └── UpdateOrderRequest.php
|
|
65
|
+
├── ViewModels/ ← INTERNAL
|
|
66
|
+
│ ├── OrderListViewModel.php ← List page view model
|
|
67
|
+
│ └── OrderDetailViewModel.php ← Detail page view model
|
|
68
|
+
├── database/
|
|
69
|
+
│ └── migrations/ ← INTERNAL
|
|
70
|
+
│ └── 2024_01_01_000000_create_orders_table.php
|
|
71
|
+
├── resources/
|
|
72
|
+
│ └── views/ ← INTERNAL
|
|
73
|
+
│ ├── pages/
|
|
74
|
+
│ │ ├── index.blade.php ← List page
|
|
75
|
+
│ │ ├── show.blade.php ← Detail page
|
|
76
|
+
│ │ ├── create.blade.php ← Create form
|
|
77
|
+
│ │ └── edit.blade.php ← Edit form
|
|
78
|
+
│ └── fragments/
|
|
79
|
+
│ ├── order-row.blade.php ← Table row fragment
|
|
80
|
+
│ └── search-results.blade.php ← Search results fragment
|
|
81
|
+
├── routes/
|
|
82
|
+
│ └── web.php ← Module routes
|
|
83
|
+
├── Config/
|
|
84
|
+
│ └── config.php ← Module config
|
|
85
|
+
├── Providers/
|
|
86
|
+
│ └── OrderServiceProvider.php ← Module service provider
|
|
87
|
+
└── Tests/
|
|
88
|
+
├── Feature/
|
|
89
|
+
└── Unit/
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Public API (Contracts, DTOs, Events, Exceptions)** — only these types are visible to
|
|
93
|
+
other modules:
|
|
94
|
+
- `OrderServiceInterface` — Interface. The sole programmatic entry point for other modules.
|
|
95
|
+
Other modules inject this interface; Laravel wires the internal `OrderService` via the
|
|
96
|
+
module's service provider.
|
|
97
|
+
- `OrderData` — The data contract. When `OrderServiceInterface` returns data, it returns
|
|
98
|
+
this DTO, never the internal `Order` Eloquent model. This prevents leaking persistence
|
|
99
|
+
details.
|
|
100
|
+
- `OrderException` — Callers can catch module-specific failures.
|
|
101
|
+
- `OrderCreatedEvent` — Domain events must be in the public namespace so listeners in
|
|
102
|
+
other modules can reference the event type.
|
|
103
|
+
|
|
104
|
+
**Internal implementation** — invisible to other modules by convention:
|
|
105
|
+
- `OrderService` — Contains all business logic, Eloquent queries, event dispatching.
|
|
106
|
+
- `Order` — The Eloquent model is internal. It may have casts, relationships, or scopes
|
|
107
|
+
that are irrelevant to consumers.
|
|
108
|
+
- `OrderRepository` — Data access abstraction (optional — some teams query directly in
|
|
109
|
+
the service).
|
|
110
|
+
- `OrderPageController` — The controller is an **inbound adapter**. Other modules never
|
|
111
|
+
call controllers.
|
|
112
|
+
- `OrderListViewModel` — View models are internal to the module.
|
|
113
|
+
|
|
114
|
+
### Module Service Provider Binding
|
|
115
|
+
|
|
116
|
+
Each module's service provider binds the interface to the implementation:
|
|
117
|
+
|
|
118
|
+
```php
|
|
119
|
+
namespace Modules\Order\Providers;
|
|
120
|
+
|
|
121
|
+
use Illuminate\Support\ServiceProvider;
|
|
122
|
+
use Modules\Order\Contracts\OrderServiceInterface;
|
|
123
|
+
use Modules\Order\Services\OrderService;
|
|
124
|
+
|
|
125
|
+
class OrderServiceProvider extends ServiceProvider
|
|
126
|
+
{
|
|
127
|
+
public function register(): void
|
|
128
|
+
{
|
|
129
|
+
$this->app->bind(OrderServiceInterface::class, OrderService::class);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public function boot(): void
|
|
133
|
+
{
|
|
134
|
+
$this->loadMigrationsFrom(module_path('Order', 'database/migrations'));
|
|
135
|
+
$this->loadViewsFrom(module_path('Order', 'resources/views'), 'order');
|
|
136
|
+
$this->loadRoutesFrom(module_path('Order', 'routes/web.php'));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Inter-module dependency direction
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
OrderPageController (internal) → OrderServiceInterface (public contract)
|
|
145
|
+
↑
|
|
146
|
+
InventoryService (another module) → OrderServiceInterface (public contract)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Both the internal controller and external modules depend on the same public interface.
|
|
150
|
+
The `OrderService` implementation is wired by Laravel's service container but never
|
|
151
|
+
directly referenced.
|
|
152
|
+
|
|
153
|
+
### What modules must NOT do
|
|
154
|
+
|
|
155
|
+
- Never inject a Service class from another module — use the Contract interface
|
|
156
|
+
- Never reference another module's `Models/`, `Services/`, or `Http/` namespace
|
|
157
|
+
- Never reference another module's Eloquent Model directly
|
|
158
|
+
- Never create circular dependencies between modules
|
|
159
|
+
- Never bypass the service interface by calling another module's controller
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Inter-Module Communication via Events
|
|
164
|
+
|
|
165
|
+
When one module needs to notify others about a domain event (e.g., an order was created
|
|
166
|
+
and inventory needs to adjust), use Laravel's event system.
|
|
167
|
+
|
|
168
|
+
### Event Class Convention
|
|
169
|
+
|
|
170
|
+
Events are plain PHP classes, immutable, and carry only the data the listener needs.
|
|
171
|
+
|
|
172
|
+
Naming: `<AggregateRoot><PastTenseVerb>Event`
|
|
173
|
+
|
|
174
|
+
```php
|
|
175
|
+
// In Modules/Order/Events/ (module public — visible to other modules)
|
|
176
|
+
namespace Modules\Order\Events;
|
|
177
|
+
|
|
178
|
+
use Illuminate\Foundation\Events\Dispatchable;
|
|
179
|
+
use Illuminate\Queue\SerializesModels;
|
|
180
|
+
|
|
181
|
+
class OrderCreatedEvent
|
|
182
|
+
{
|
|
183
|
+
use Dispatchable, SerializesModels;
|
|
184
|
+
|
|
185
|
+
public function __construct(
|
|
186
|
+
public readonly string $orderId,
|
|
187
|
+
public readonly string $customerId,
|
|
188
|
+
public readonly \DateTimeImmutable $occurredAt,
|
|
189
|
+
) {}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Why readonly properties: they're immutable and make the event's contract explicit.
|
|
194
|
+
Using `DateTimeImmutable` establishes when the event happened.
|
|
195
|
+
|
|
196
|
+
### Publishing Events
|
|
197
|
+
|
|
198
|
+
The service implementation (internal) publishes events using Laravel's `event()` helper
|
|
199
|
+
or `Event` facade:
|
|
200
|
+
|
|
201
|
+
```php
|
|
202
|
+
// In Modules/Order/Services/ (INTERNAL)
|
|
203
|
+
namespace Modules\Order\Services;
|
|
204
|
+
|
|
205
|
+
use Modules\Order\Contracts\OrderServiceInterface;
|
|
206
|
+
use Modules\Order\DTOs\OrderData;
|
|
207
|
+
use Modules\Order\Events\OrderCreatedEvent;
|
|
208
|
+
use Modules\Order\Models\Order;
|
|
209
|
+
|
|
210
|
+
class OrderService implements OrderServiceInterface
|
|
211
|
+
{
|
|
212
|
+
public function create(array $data): OrderData
|
|
213
|
+
{
|
|
214
|
+
$order = Order::create($data);
|
|
215
|
+
|
|
216
|
+
event(new OrderCreatedEvent(
|
|
217
|
+
orderId: $order->id,
|
|
218
|
+
customerId: $order->customer_id,
|
|
219
|
+
occurredAt: new \DateTimeImmutable(),
|
|
220
|
+
));
|
|
221
|
+
|
|
222
|
+
return OrderData::from($order);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Listening to Events
|
|
228
|
+
|
|
229
|
+
Other modules listen using Laravel listeners in their service provider:
|
|
230
|
+
|
|
231
|
+
```php
|
|
232
|
+
// In Modules/Inventory/Listeners/ (INTERNAL)
|
|
233
|
+
namespace Modules\Inventory\Listeners;
|
|
234
|
+
|
|
235
|
+
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
236
|
+
use Modules\Order\Events\OrderCreatedEvent;
|
|
237
|
+
|
|
238
|
+
class AdjustInventoryOnOrderCreated implements ShouldQueue
|
|
239
|
+
{
|
|
240
|
+
public bool $afterCommit = true;
|
|
241
|
+
|
|
242
|
+
public function handle(OrderCreatedEvent $event): void
|
|
243
|
+
{
|
|
244
|
+
// Business logic to reserve inventory
|
|
245
|
+
logger()->info("Adjusting inventory for order: {$event->orderId}");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Register in the module's `EventServiceProvider` or `boot()`:
|
|
251
|
+
```php
|
|
252
|
+
// In Modules/Inventory/Providers/InventoryServiceProvider.php
|
|
253
|
+
use Illuminate\Support\Facades\Event;
|
|
254
|
+
use Modules\Inventory\Listeners\AdjustInventoryOnOrderCreated;
|
|
255
|
+
use Modules\Order\Events\OrderCreatedEvent;
|
|
256
|
+
|
|
257
|
+
public function boot(): void
|
|
258
|
+
{
|
|
259
|
+
Event::listen(OrderCreatedEvent::class, AdjustInventoryOnOrderCreated::class);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The listener references `OrderCreatedEvent` which is in the `Order` module's public
|
|
264
|
+
`Events/` namespace, so this cross-module reference is valid. The listener implementation
|
|
265
|
+
itself is internal to the `Inventory` module.
|
|
266
|
+
|
|
267
|
+
`ShouldQueue` + `$afterCommit = true` means:
|
|
268
|
+
- The listener runs in a separate queue job
|
|
269
|
+
- It executes after the publishing transaction commits
|
|
270
|
+
- Failures in the listener do not roll back the publisher
|
|
271
|
+
|
|
272
|
+
### Event Publication Persistence
|
|
273
|
+
|
|
274
|
+
For at-least-once delivery semantics, consider:
|
|
275
|
+
1. Using Laravel's queue system with database driver (failed jobs are retried)
|
|
276
|
+
2. Implementing a custom `event_publications` table for tracking delivery status
|
|
277
|
+
3. Using `spatie/laravel-event-sourcing` for full event sourcing
|
|
278
|
+
|
|
279
|
+
Configuration in `config/queue.php`:
|
|
280
|
+
```php
|
|
281
|
+
'connections' => [
|
|
282
|
+
'database' => [
|
|
283
|
+
'driver' => 'database',
|
|
284
|
+
'table' => 'jobs',
|
|
285
|
+
'queue' => 'default',
|
|
286
|
+
'retry_after' => 90,
|
|
287
|
+
'after_commit' => true,
|
|
288
|
+
],
|
|
289
|
+
],
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Module Verification
|
|
295
|
+
|
|
296
|
+
To verify module boundaries are respected, use `phpat/phpat` architecture tests:
|
|
297
|
+
|
|
298
|
+
```php
|
|
299
|
+
namespace Tests\Architecture;
|
|
300
|
+
|
|
301
|
+
use PHPat\Selector\Selector;
|
|
302
|
+
use PHPat\Test\Builder\Rule;
|
|
303
|
+
use PHPat\Test\PHPat;
|
|
304
|
+
|
|
305
|
+
class ModuleBoundaryTest
|
|
306
|
+
{
|
|
307
|
+
public function test_modules_only_depend_on_public_api(): Rule
|
|
308
|
+
{
|
|
309
|
+
return PHPat::rule()
|
|
310
|
+
->classes(Selector::inNamespace('Modules\Inventory'))
|
|
311
|
+
->canOnlyDependOn()
|
|
312
|
+
->classes(
|
|
313
|
+
Selector::inNamespace('Modules\Order\Contracts'),
|
|
314
|
+
Selector::inNamespace('Modules\Order\DTOs'),
|
|
315
|
+
Selector::inNamespace('Modules\Order\Events'),
|
|
316
|
+
Selector::inNamespace('Modules\Order\Exceptions'),
|
|
317
|
+
// ... other public namespaces
|
|
318
|
+
Selector::inNamespace('Illuminate'),
|
|
319
|
+
Selector::inNamespace('App'),
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
This test catches:
|
|
326
|
+
- Direct dependencies between modules that bypass contracts
|
|
327
|
+
- Circular module dependencies
|
|
328
|
+
- References to another module's internal types (Models, Services, Http)
|
|
329
|
+
|
|
330
|
+
Include this test in the Testing Strategy section of the spec. It should run as part
|
|
331
|
+
of CI to enforce architectural integrity continuously.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Web Application Module Adaptations
|
|
336
|
+
|
|
337
|
+
For server-rendered web applications, each module includes `pages/` and `fragments/`
|
|
338
|
+
view directories, page and fragment controllers, and view models.
|
|
339
|
+
|
|
340
|
+
### Key Patterns
|
|
341
|
+
|
|
342
|
+
1. **No REST controllers**: Web modules use standard `Controller` returning Blade views,
|
|
343
|
+
not JSON API responses.
|
|
344
|
+
|
|
345
|
+
2. **Direct data ownership**: Each module owns its Eloquent models and migrations.
|
|
346
|
+
The service layer queries the database directly via Eloquent.
|
|
347
|
+
|
|
348
|
+
3. **View models**: Each page has a dedicated view model class (`spatie/laravel-view-models`)
|
|
349
|
+
that encapsulates all data needed by the Blade template. View models are internal.
|
|
350
|
+
|
|
351
|
+
4. **Page vs Fragment separation**: `pages/` holds Blade views for full page renders;
|
|
352
|
+
`fragments/` holds Blade views that return partial HTML for htmx requests.
|
|
353
|
+
|
|
354
|
+
5. **Form Requests**: Controllers use Form Request classes for validation. Validation
|
|
355
|
+
rules are defined per-action (Store, Update, etc.).
|
|
356
|
+
|
|
357
|
+
### Web Page Controller Pattern
|
|
358
|
+
|
|
359
|
+
```php
|
|
360
|
+
namespace Modules\Order\Http\Controllers;
|
|
361
|
+
|
|
362
|
+
use Illuminate\Http\Request;
|
|
363
|
+
use Illuminate\View\View;
|
|
364
|
+
use Modules\Order\Contracts\OrderServiceInterface;
|
|
365
|
+
use Modules\Order\ViewModels\OrderListViewModel;
|
|
366
|
+
use Modules\Order\ViewModels\OrderDetailViewModel;
|
|
367
|
+
|
|
368
|
+
class OrderPageController extends Controller
|
|
369
|
+
{
|
|
370
|
+
public function __construct(
|
|
371
|
+
private readonly OrderServiceInterface $orderService,
|
|
372
|
+
) {}
|
|
373
|
+
|
|
374
|
+
public function index(Request $request): View
|
|
375
|
+
{
|
|
376
|
+
$items = $this->orderService->list(
|
|
377
|
+
page: $request->integer('page', 1),
|
|
378
|
+
perPage: 20,
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
return (new OrderListViewModel(
|
|
382
|
+
items: $items,
|
|
383
|
+
theme: $request->cookie('theme', 'light'),
|
|
384
|
+
user: auth()->user(),
|
|
385
|
+
))->view('order::pages.index');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
public function show(string $id, Request $request): View
|
|
389
|
+
{
|
|
390
|
+
$order = $this->orderService->get($id);
|
|
391
|
+
|
|
392
|
+
return (new OrderDetailViewModel(
|
|
393
|
+
order: $order,
|
|
394
|
+
theme: $request->cookie('theme', 'light'),
|
|
395
|
+
user: auth()->user(),
|
|
396
|
+
))->view('order::pages.show');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Web Fragment Controller Pattern
|
|
402
|
+
|
|
403
|
+
```php
|
|
404
|
+
namespace Modules\Order\Http\Controllers;
|
|
405
|
+
|
|
406
|
+
use Illuminate\Http\Request;
|
|
407
|
+
use Illuminate\Http\Response;
|
|
408
|
+
use Illuminate\View\View;
|
|
409
|
+
use Modules\Order\Contracts\OrderServiceInterface;
|
|
410
|
+
|
|
411
|
+
class OrderFragmentController extends Controller
|
|
412
|
+
{
|
|
413
|
+
public function __construct(
|
|
414
|
+
private readonly OrderServiceInterface $orderService,
|
|
415
|
+
) {}
|
|
416
|
+
|
|
417
|
+
public function row(string $id): View
|
|
418
|
+
{
|
|
419
|
+
$order = $this->orderService->get($id);
|
|
420
|
+
return view('order::fragments.order-row', ['item' => $order]);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
public function search(Request $request): View
|
|
424
|
+
{
|
|
425
|
+
$results = $this->orderService->search($request->input('q', ''));
|
|
426
|
+
return view('order::fragments.search-results', ['items' => $results]);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
public function destroy(string $id): Response
|
|
430
|
+
{
|
|
431
|
+
$this->orderService->delete($id);
|
|
432
|
+
return response('', 200);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Module Route File
|
|
438
|
+
|
|
439
|
+
```php
|
|
440
|
+
// Modules/Order/routes/web.php
|
|
441
|
+
use Illuminate\Support\Facades\Route;
|
|
442
|
+
use Modules\Order\Http\Controllers\OrderPageController;
|
|
443
|
+
use Modules\Order\Http\Controllers\OrderFragmentController;
|
|
444
|
+
|
|
445
|
+
Route::middleware(['auth', 'role:HUB_ADMINISTRATOR|HUB_OPERATION_SUPPORT'])
|
|
446
|
+
->group(function () {
|
|
447
|
+
// Page routes
|
|
448
|
+
Route::get('/orders', [OrderPageController::class, 'index'])->name('order.index');
|
|
449
|
+
Route::get('/orders/create', [OrderPageController::class, 'create'])->name('order.create');
|
|
450
|
+
Route::post('/orders', [OrderPageController::class, 'store'])->name('order.store');
|
|
451
|
+
Route::get('/orders/{id}', [OrderPageController::class, 'show'])->name('order.show');
|
|
452
|
+
Route::get('/orders/{id}/edit', [OrderPageController::class, 'edit'])->name('order.edit');
|
|
453
|
+
Route::put('/orders/{id}', [OrderPageController::class, 'update'])->name('order.update');
|
|
454
|
+
Route::delete('/orders/{id}', [OrderPageController::class, 'destroy'])->name('order.destroy');
|
|
455
|
+
|
|
456
|
+
// Fragment routes (htmx)
|
|
457
|
+
Route::get('/orders/fragments/search', [OrderFragmentController::class, 'search'])->name('order.fragments.search');
|
|
458
|
+
Route::get('/orders/fragments/row/{id}', [OrderFragmentController::class, 'row'])->name('order.fragments.row');
|
|
459
|
+
Route::delete('/orders/fragments/{id}', [OrderFragmentController::class, 'destroy'])->name('order.fragments.destroy');
|
|
460
|
+
});
|
|
461
|
+
```
|