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 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,15 @@
1
+ module Rampart
2
+ module Application
3
+ class Transaction
4
+ def initialize(adapter)
5
+ @adapter = adapter
6
+ end
7
+
8
+ def call(&block)
9
+ @adapter.call(&block)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+
@@ -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,15 @@
1
+ module Rampart
2
+ module Domain
3
+ class DomainException < StandardError
4
+ attr_reader :code, :context
5
+
6
+ def initialize(message, code: nil, context: {})
7
+ @code = code
8
+ @context = context
9
+ super(message)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+
@@ -0,0 +1,10 @@
1
+ module Rampart
2
+ module Domain
3
+ class DomainService
4
+ # Base class for domain services
5
+ # Domain services contain domain logic that doesn't naturally fit within an entity or value object
6
+ end
7
+ end
8
+ end
9
+
10
+
@@ -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
+