rampart-core 0.1.1
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.
- checksums.yaml +7 -0
- data/LICENSE +191 -0
- data/README.md +296 -0
- data/lib/rampart/application/command.rb +15 -0
- data/lib/rampart/application/query.rb +15 -0
- data/lib/rampart/application/service.rb +26 -0
- data/lib/rampart/application/transaction.rb +15 -0
- data/lib/rampart/domain/aggregate_root.rb +13 -0
- data/lib/rampart/domain/domain_event.rb +24 -0
- data/lib/rampart/domain/domain_exception.rb +15 -0
- data/lib/rampart/domain/domain_service.rb +10 -0
- data/lib/rampart/domain/entity.rb +20 -0
- data/lib/rampart/domain/value_object.rb +12 -0
- data/lib/rampart/engine_loader.rb +101 -0
- data/lib/rampart/ports/event_bus_port.rb +10 -0
- data/lib/rampart/ports/secondary_port.rb +13 -0
- data/lib/rampart/support/container.rb +28 -0
- data/lib/rampart/support/result.rb +9 -0
- data/lib/rampart/support/types.rb +9 -0
- data/lib/rampart/testing/architecture_matchers.rb +279 -0
- data/lib/rampart/testing/engine_architecture_shared_spec.rb +345 -0
- data/lib/rampart/testing.rb +15 -0
- data/lib/rampart/version.rb +5 -0
- data/lib/rampart.rb +28 -0
- metadata +152 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 682f667118536ae6d0cf6abad03bb538ff90be199a23e84c7e974e77fb3b6e64
|
|
4
|
+
data.tar.gz: 8abec9aef51617a79947b093e324d3abc5ea69d77cd3ca8980cafc11be26baea
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 712c02a299f9c124a6777419fb6ff78cced66b27a968124612829f4481a88a74b1e5bc48b03e66df25d6b70932510512f63e49f69a2f12453f2d3cdcf1162492
|
|
7
|
+
data.tar.gz: c5dd7eeec8abd6eec76ab832e97bf8967b96a27cfd3a071bd546cd99a926c3061df0cf9c646e9982758217c5e7a900693bd5c020bb9b194e8178b355098769ca
|
data/LICENSE
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
Copyright 2025 Rampart Team
|
|
179
|
+
|
|
180
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
|
+
you may not use this file except in compliance with the License.
|
|
182
|
+
You may obtain a copy of the License at
|
|
183
|
+
|
|
184
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
185
|
+
|
|
186
|
+
Unless required by applicable law or agreed to in writing, software
|
|
187
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
188
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
|
+
See the License for the specific language governing permissions and
|
|
190
|
+
limitations under the License.
|
|
191
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Rampart: Architecture on Rails
|
|
2
|
+
|
|
3
|
+
An Architecture-as-Code toolkit designed to bring disciplined, maintainable architecture to Rails applications—without abandoning the productivity of the monolith.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
### What problem does Rampart solve?
|
|
10
|
+
|
|
11
|
+
Organizations choose Rails because it's fast. A small team can ship features at remarkable speed, and the monolith keeps things simple.
|
|
12
|
+
|
|
13
|
+
Then the organization grows. A handful of developers become a hundred. Many teams work across many parts of the codebase. The original architects leave—and even those who stay can no longer keep the whole picture in their heads. Without explicit boundaries, entropy takes hold. The codebase becomes a big ball of mud: tangled dependencies, unclear ownership, and changes that ripple unpredictably across the system.
|
|
14
|
+
|
|
15
|
+
The company adopts AI-assisted development hoping to accelerate delivery, only to discover it makes everything worse. AI tools generate code quickly but don't understand architectural intent. They hallucinate dependencies, flatten carefully designed layers, and ignore boundaries—introducing structural inconsistencies faster than teams can catch them.
|
|
16
|
+
|
|
17
|
+
Rampart addresses both the scaling challenge and the AI challenge by providing:
|
|
18
|
+
|
|
19
|
+
- **Declarative architecture blueprints** that serve as the single source of truth for system structure
|
|
20
|
+
- **Nine curated architectural patterns**—centered on Domain-Driven Design and Hexagonal Architecture—that maximize both team autonomy and code clarity
|
|
21
|
+
- **AI-native design** with machine-readable formats that enable AI agents to understand and respect architectural boundaries
|
|
22
|
+
- **A Terraform-like workflow** for designing, scaffolding, validating, and evolving architecture over time
|
|
23
|
+
|
|
24
|
+
### What are the core goals of Rampart?
|
|
25
|
+
|
|
26
|
+
**Goal 1 — Bounded Context & Stream-Aligned Team Effectiveness**
|
|
27
|
+
|
|
28
|
+
Teams should be able to deliver end-to-end features *within a single bounded context* with minimal coordination across contexts.
|
|
29
|
+
|
|
30
|
+
**Goal 2 — Human & AI Code Clarity**
|
|
31
|
+
|
|
32
|
+
Code should be predictable, structured, and easy for both humans and AIs to understand, navigate, and maintain.
|
|
33
|
+
|
|
34
|
+
### Does Rampart replace Rails?
|
|
35
|
+
|
|
36
|
+
No. Rampart is not a Rails rewrite or alternative. It works *with* Rails, not against it.
|
|
37
|
+
|
|
38
|
+
Rampart provides architectural discipline on top of Rails. It enforces structure for bounded contexts, domain models, and application layers while remaining deliberately agnostic about testing frameworks, linters, ORMs, and other peripheral concerns.
|
|
39
|
+
|
|
40
|
+
### Is there another tool that already does this?
|
|
41
|
+
|
|
42
|
+
No. While several tools address pieces of this problem, none provide the full Architecture-as-Code lifecycle that Rampart offers.
|
|
43
|
+
|
|
44
|
+
Below are important capability gaps that current tools do not fill, along with examples of tools that come closest but still fall short:
|
|
45
|
+
|
|
46
|
+
| Missing Capability | Most Similar Tools | Why They Fall Short |
|
|
47
|
+
| ----------------------------------------------------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| **A single authoritative, version-controlled architecture blueprint** | Structurizr, PlantUML, C4 model tools | These generate diagrams or model visualizations, but do not serve as living, enforced architectural specifications or a system of record. |
|
|
49
|
+
| **Planning + structural change workflow (similar to Terraform plan)** | OpenAPI/AsyncAPI (for API planning), Backstage templates | OpenAPI only covers API layers; Backstage handles service metadata, not domain structure or architectural decisions. No tool plans architectural scaffolding or migrations. |
|
|
50
|
+
| **Sync between architecture and code, including drift detection** | ArchUnit, dependency-analysis tools, SonarQube | These can detect certain code smells or dependency violations but cannot compare code to a declarative architecture model or enforce layered/domain boundaries. |
|
|
51
|
+
| **DDD-native domain modeling (bounded contexts, aggregates, ports/adapters)** | ContextMapper (academic), Structurizr DSL | These express relationships conceptually but lack Rails integration, scaffolding, JSON blueprints, or synchronization with real codebases. |
|
|
52
|
+
| **Architecture-guided scaffolding without generating business logic** | Rails generators, JHipster | Generators scaffold code quickly but do not encode or enforce architectural boundaries; they cannot evolve designs over time or keep architecture synchronized. |
|
|
53
|
+
| **Migration planning from legacy codebases into modern architecture** | vFunction (for Java), monolith decomposition tools | Focused on technical decomposition or microservices extraction, not DDD-based restructuring, JSON blueprints, or Rails codebases. |
|
|
54
|
+
| **AI-native architecture understanding and enforcement** | None | No current tool provides machine-parseable architecture blueprints designed specifically for LLM agents to use while writing or modifying code. |
|
|
55
|
+
|
|
56
|
+
**The gap**: While certain tools overlap partially with Rampart's goals, none provide a full Architecture-as-Code lifecycle with DDD semantics, scaffolding, drift detection, and AI-native integration.
|
|
57
|
+
|
|
58
|
+
### Where is Rampart opinionated?
|
|
59
|
+
|
|
60
|
+
Rampart enforces specific architectural choices where clarity and consistency are essential:
|
|
61
|
+
|
|
62
|
+
- **The Nine Architecture Patterns**: DDD bounded contexts, Hexagonal Architecture layering, use case modeling, and event-driven patterns represent the canonical way to structure Rampart applications
|
|
63
|
+
- **Monorepo Structure**: All bounded contexts must reside in a single monorepo for centralized architecture governance
|
|
64
|
+
- **Rails Engine Implementation**: Bounded contexts must be implemented as Rails engines for clear module boundaries. (In the future, it will support microservices)
|
|
65
|
+
- **JSON for Architecture Blueprints**: Blueprints use JSON (not YAML) for AI-safety and deterministic parsing
|
|
66
|
+
- **Dry-rb Foundation**: Built on the dry-rb ecosystem for type safety, immutability, and functional patterns
|
|
67
|
+
- **Packwerk Static Analysis**: Enforces layer boundaries (domain/application/infrastructure) and cross-context dependencies
|
|
68
|
+
- **Rspec Fitness Function Enforcement**: RSpec shared specs verify runtime dependency boundaries, base class contracts, immutability, and JSON blueprint synchronization
|
|
69
|
+
|
|
70
|
+
### Where is Rampart unopinionated?
|
|
71
|
+
|
|
72
|
+
Teams bring their own preferences for:
|
|
73
|
+
|
|
74
|
+
- Code formatters and linters (StandardRB, RuboCop, etc.)
|
|
75
|
+
- ORMs and databases (ActiveRecord, ROM, Sequel)
|
|
76
|
+
- Web frameworks (Rails, Hanami, Sinatra)
|
|
77
|
+
- Background job processors (Sidekiq, GoodJob, etc.)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Architecture & Design
|
|
82
|
+
|
|
83
|
+
### What architecture patterns does Rampart use?
|
|
84
|
+
|
|
85
|
+
Rampart provides nine enforceable, AI-friendly architecture patterns:
|
|
86
|
+
|
|
87
|
+
1. **Domain-Driven Design** — Align software models with business domains using bounded contexts and tactical patterns
|
|
88
|
+
2. **Hexagonal Architecture** — Isolate domain logic from infrastructure through ports and adapters
|
|
89
|
+
3. **Clean Architecture / Onion** — Enforce strict dependency direction with framework-independent core
|
|
90
|
+
4. **Modular Monolith / Vertical Slices** — Organize code into autonomous vertical slices within a single deployable
|
|
91
|
+
5. **Lightweight CQRS & Task-Based Interfaces** — Separate read/write models with intent-revealing commands
|
|
92
|
+
6. **Domain Events & Event-Driven Modeling** — Decouple bounded contexts through immutable business facts
|
|
93
|
+
7. **Architecture Fitness Functions** — Validate architectural rules programmatically in CI/CD
|
|
94
|
+
8. **Functional Core / Imperative Shell** — Keep domain logic pure and side-effect-free
|
|
95
|
+
9. **C4 Model (Inside-the-Box)** — Visualize system structure at multiple levels of abstraction
|
|
96
|
+
|
|
97
|
+
For detailed explanations, anti-patterns, and implementation guidance, see [Architecture Philosophy](docs/rampart_architecture_philosophy.md).
|
|
98
|
+
|
|
99
|
+
### How does Rampart work with AI tools?
|
|
100
|
+
|
|
101
|
+
Rampart takes a two-pronged approach to AI-assisted development:
|
|
102
|
+
|
|
103
|
+
**1. Prompt-Driven Guidance** — Rampart ships **prompt files** that encode architectural knowledge:
|
|
104
|
+
- `architecture.prompt.md` — Guides the design of `architecture.json` through collaborative dialogue
|
|
105
|
+
- `planning.prompt.md` — Guides spec completion for capabilities, gathering functional and technical requirements
|
|
106
|
+
|
|
107
|
+
Running `rampart init` installs these as slash commands (`/rampart.architect` and `/rampart.plan`) for both Cursor and Claude Code.
|
|
108
|
+
|
|
109
|
+
**2. Validation & Enforcement** — Rampart validates that code adheres to architecture:
|
|
110
|
+
- **Post-hoc verification** — After code changes, Packwerk and RSpec architecture specs confirm the architecture remains intact
|
|
111
|
+
- **Drift detection** — `rampart sync` identifies divergence between code and `architecture.json`
|
|
112
|
+
- **Blame-free feedback** — When violations are detected, Rampart provides actionable guidance for correction
|
|
113
|
+
|
|
114
|
+
This dual approach means AI agents are guided proactively through prompts, then verified through enforcement tools.
|
|
115
|
+
|
|
116
|
+
### How is Rampart like Terraform?
|
|
117
|
+
|
|
118
|
+
Rampart can be viewed as a "Terraform for application architecture":
|
|
119
|
+
|
|
120
|
+
| Terraform Concept | Rampart Equivalent |
|
|
121
|
+
| -------------------- | --------------------------------------------------------- |
|
|
122
|
+
| Resource definitions | Use cases, events, domain objects, adapters |
|
|
123
|
+
| Dependency graph | Event flows, inter-BC relationships, C4 components |
|
|
124
|
+
| Validate | Fitness functions (Packwerk + RSpec) |
|
|
125
|
+
| Plan | Spec generation (`rampart spec`) + prompt-guided planning |
|
|
126
|
+
| Apply | Prompt-guided spec completion + implementation |
|
|
127
|
+
| State file | Rampart architecture JSON blueprints |
|
|
128
|
+
| Import | Legacy extraction (`rampart extract`) |
|
|
129
|
+
|
|
130
|
+
For the complete Terraform analogy, see [Architecture Philosophy](docs/rampart_architecture_philosophy.md#311-the-terraform-analogy).
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Getting Started
|
|
135
|
+
|
|
136
|
+
### How do I install the Rampart gem?
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
gem 'rampart', path: '../rampart' # For local development
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Or from a gem server (when published):
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
gem 'rampart'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### What functionality does the gem provide?
|
|
149
|
+
|
|
150
|
+
The Rampart gem provides base classes for implementing DDD and Hexagonal Architecture patterns:
|
|
151
|
+
|
|
152
|
+
- **Domain layer primitives** — Aggregates, entities, value objects, domain events, and domain services with built-in immutability and type safety
|
|
153
|
+
- **Application layer patterns** — Commands, queries, and application services for use case orchestration
|
|
154
|
+
- **Port abstractions** — Base classes for defining and implementing hexagonal ports and adapters
|
|
155
|
+
- **Type system** — Built on dry-types for runtime validation with monadic error handling via dry-monads
|
|
156
|
+
|
|
157
|
+
### How do I use the Rampart CLI?
|
|
158
|
+
|
|
159
|
+
Install the CLI (requires [Bun](https://bun.sh)):
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cd rampart/cli && bun install
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Then run with:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
bun run rampart --help
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### What features does the Rampart CLI provide?
|
|
172
|
+
|
|
173
|
+
The Rampart CLI manages the full architecture lifecycle:
|
|
174
|
+
|
|
175
|
+
- **Initialize & scaffold** — Bootstrap projects with `rampart init`, create bounded contexts, and generate DDD directory structures
|
|
176
|
+
- **Generate specs** — `rampart spec` reads `architecture.json` and generates spec templates for each capability
|
|
177
|
+
- **Validate & sync** — Detect drift between code and architecture definitions with `rampart sync`
|
|
178
|
+
- **Migrate legacy** — Extract domain models and create phased migration plans for existing codebases
|
|
179
|
+
|
|
180
|
+
For complete CLI documentation, see [Features](docs/rampart_features.md#cli-tools-).
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
### What does a typical Rampart project look like?
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
project-root/
|
|
187
|
+
├── architecture/
|
|
188
|
+
│ ├── system.json # System-level manifest
|
|
189
|
+
│ ├── catalog/ # Per-bounded-context directory
|
|
190
|
+
│ │ ├── architecture.json
|
|
191
|
+
│ │ ├── browse_catalog.spec.md
|
|
192
|
+
│ │ └── manage_catalog.spec.md
|
|
193
|
+
│ └── payments/
|
|
194
|
+
│ ├── architecture.json
|
|
195
|
+
│ └── ...
|
|
196
|
+
├── prompts/
|
|
197
|
+
│ ├── architecture.prompt.md # Guides architecture.json design
|
|
198
|
+
│ └── planning.prompt.md # Guides spec completion
|
|
199
|
+
├── docs/
|
|
200
|
+
│ ├── diagrams/ # Architecture diagrams
|
|
201
|
+
│ │ ├── catalog_architecture.md
|
|
202
|
+
│ │ └── images/
|
|
203
|
+
├── engines/
|
|
204
|
+
│ ├── catalog/ # Bounded context as Rails engine
|
|
205
|
+
│ └── payments/
|
|
206
|
+
└── apps/
|
|
207
|
+
└── web/ # Rails app
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Each engine follows the DDD/Hexagonal structure while respecting Rails conventions:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
engines/catalog/ # "Catalog" bounded context
|
|
214
|
+
├── app/
|
|
215
|
+
│ ├── controllers/catalog # Controller infrastructure
|
|
216
|
+
│ ├── models/catalog # ActiveRecord infrastructure
|
|
217
|
+
│ │
|
|
218
|
+
│ ├── domain/catalog/ # Domain layer
|
|
219
|
+
│ │ ├── aggregates/
|
|
220
|
+
│ │ ├── entities/
|
|
221
|
+
│ │ ├── value_objects/
|
|
222
|
+
│ │ ├── events/
|
|
223
|
+
│ │ ├── services/
|
|
224
|
+
│ │ └── ports/
|
|
225
|
+
│ │
|
|
226
|
+
│ ├── application/catalog/ # Application layer
|
|
227
|
+
│ │ ├── services/
|
|
228
|
+
│ │ ├── commands/
|
|
229
|
+
│ │ └── queries/
|
|
230
|
+
│ │
|
|
231
|
+
│ └── infrastructure/catalog/ # Infrastructure layer (except Rails infrastructure as noted above)
|
|
232
|
+
│ ├── persistence/
|
|
233
|
+
│ │ ├── mappers/
|
|
234
|
+
│ │ └── repositories/
|
|
235
|
+
│ ├── http/
|
|
236
|
+
│ │ └── serializers/
|
|
237
|
+
│ ├── adapters/
|
|
238
|
+
│ └── wiring/
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
For complete project structure details, see [System Overview](docs/rampart_system.md).
|
|
242
|
+
|
|
243
|
+
### Can I see some example code snippets?
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
# Domain Layer - Pure Ruby
|
|
247
|
+
class Order < Rampart::Domain::AggregateRoot
|
|
248
|
+
attribute :id, Types::String
|
|
249
|
+
attribute :items, Types::Array.default([].freeze)
|
|
250
|
+
|
|
251
|
+
def add_item(product, quantity)
|
|
252
|
+
updated_items = items + [LineItem.new(product: product, quantity: quantity)]
|
|
253
|
+
self.class.new(**attributes.merge(items: updated_items))
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Application Layer - Use Cases
|
|
258
|
+
class CreateOrderService < Rampart::Application::Service
|
|
259
|
+
include Dry::Monads[:result]
|
|
260
|
+
|
|
261
|
+
def call(command)
|
|
262
|
+
order = Order.create(id: generate_id, customer_id: command.customer_id)
|
|
263
|
+
persisted = @order_repo.save(order)
|
|
264
|
+
Success(persisted)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Infrastructure Layer - Adapters
|
|
269
|
+
class SqlOrderRepository < Rampart::Ports::SecondaryPort
|
|
270
|
+
def save(order)
|
|
271
|
+
OrderMapper.to_record(order).save!
|
|
272
|
+
order
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Learning More
|
|
280
|
+
|
|
281
|
+
### Where can I find documentation?
|
|
282
|
+
|
|
283
|
+
- **[Architecture Philosophy](docs/rampart_architecture_philosophy.md)** - Core principles, patterns, and anti-patterns
|
|
284
|
+
- **[User Guide](docs/rampart_user_guide.md)** - Day-to-day usage and best practices
|
|
285
|
+
- **[Features](docs/rampart_features.md)** - Complete feature list, CLI tools, and implementation details
|
|
286
|
+
- **[System Overview](docs/rampart_system.md)** - System architecture and component relationships
|
|
287
|
+
|
|
288
|
+
### Is there a demo application?
|
|
289
|
+
|
|
290
|
+
**[Cats-as-a-Service](https://github.com/pcaplan/cats-as-a-service)** - A complete Rails + Next.js e-commerce application demonstrating Rampart patterns in practice with bounded context engines, clean architecture, and DDD tactical patterns.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
Apache License 2.0
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require "dry-struct"
|
|
2
|
+
|
|
3
|
+
module Rampart
|
|
4
|
+
module Application
|
|
5
|
+
# Base DTO for CQRS command objects.
|
|
6
|
+
# Commands represent task-based, intent-revealing write operations
|
|
7
|
+
# (e.g., ShipOrder, ArchiveCustomCat) and should wrap all inputs needed
|
|
8
|
+
# to perform a single business action. Dry::Struct gives immutability
|
|
9
|
+
# and coarse type validation so command handlers receive predictable data.
|
|
10
|
+
class Command < Dry::Struct
|
|
11
|
+
transform_keys(&:to_sym)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require "dry-struct"
|
|
2
|
+
|
|
3
|
+
module Rampart
|
|
4
|
+
module Application
|
|
5
|
+
# Base DTO for CQRS queries.
|
|
6
|
+
# Queries capture read-only concerns such as pagination or filters
|
|
7
|
+
# and can be tailored for optimized projections without touching
|
|
8
|
+
# the domain model. Dry::Struct ensures query parameters are coerced
|
|
9
|
+
# to the expected types before they reach query handlers.
|
|
10
|
+
class Query < Dry::Struct
|
|
11
|
+
transform_keys(&:to_sym)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "dry-monads"
|
|
2
|
+
|
|
3
|
+
module Rampart
|
|
4
|
+
module Application
|
|
5
|
+
# Base class for application services (use cases).
|
|
6
|
+
#
|
|
7
|
+
# Services should include their bounded context's Import module for
|
|
8
|
+
# automatic dependency injection via dry-auto_inject.
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
# class CatListingService < Rampart::Application::Service
|
|
12
|
+
# include CatContent::Import[:cat_listing_repo, :id_generator, :clock, :transaction, :event_bus]
|
|
13
|
+
#
|
|
14
|
+
# def create(command)
|
|
15
|
+
# # Dependencies are automatically injected as @cat_listing_repo, etc.
|
|
16
|
+
# @transaction.call do
|
|
17
|
+
# # ...
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
class Service
|
|
22
|
+
include Dry::Monads[:result]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Rampart
|
|
2
|
+
module Domain
|
|
3
|
+
class AggregateRoot < Entity
|
|
4
|
+
# Aggregates are immutable. Domain methods return new instances instead of mutating state.
|
|
5
|
+
# Application services publish events after persistence rather than accumulating them here.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# def publish
|
|
9
|
+
# self.class.new(**attributes.merge(visibility: Visibility.public))
|
|
10
|
+
# end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "dry-struct"
|
|
2
|
+
require "securerandom"
|
|
3
|
+
|
|
4
|
+
module Rampart
|
|
5
|
+
module Domain
|
|
6
|
+
# Domain events record meaningful business changes that already happened.
|
|
7
|
+
# Aggregates remain immutable and side-effect free; application services publish events
|
|
8
|
+
# after persisting new aggregate state. Events are past-tense, immutable Dry::Structs
|
|
9
|
+
# with versioning baked in.
|
|
10
|
+
class DomainEvent < Dry::Struct
|
|
11
|
+
# Schema version enables event evolution without breaking consumers.
|
|
12
|
+
# Increment when adding/removing/renaming attributes.
|
|
13
|
+
SCHEMA_VERSION = 1
|
|
14
|
+
|
|
15
|
+
attribute :event_id, Types::String.default { SecureRandom.uuid }
|
|
16
|
+
attribute :occurred_at, Types::Time.default { Time.now }
|
|
17
|
+
attribute :schema_version, Types::Integer.default { self::SCHEMA_VERSION }
|
|
18
|
+
|
|
19
|
+
def event_type
|
|
20
|
+
self.class.name
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "dry-struct"
|
|
2
|
+
|
|
3
|
+
module Rampart
|
|
4
|
+
module Domain
|
|
5
|
+
class Entity < Dry::Struct
|
|
6
|
+
# Entities have identity and can be compared by id
|
|
7
|
+
def ==(other)
|
|
8
|
+
other.is_a?(self.class) && other.id == id
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
alias_method :eql?, :==
|
|
12
|
+
|
|
13
|
+
def hash
|
|
14
|
+
[self.class, id].hash
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|