Skip to content

Commit 0e71e4a

Browse files
authored
feat: added inventory-orders-service example (#1)
1 parent 4769258 commit 0e71e4a

37 files changed

+2714
-20
lines changed

.claude/settings.local.json

Lines changed: 0 additions & 18 deletions
This file was deleted.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ bin/
4343
### Mac OS ###
4444
.DS_Store
4545
/graalvm/graalvm-0.0.1-SNAPSHOT
46+
47+
### Claude ###
48+
.claude/

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ But you can also run it with your own infrastructure.
3737

3838
## Index of Examples
3939

40-
| **Example Project** | **Description** |
41-
|----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
40+
| **Example Project** | **Description** |
41+
|--------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
42+
| **[inventory-orders-service](inventory-orders-service/README.md)** | Example that simulates an **e-commerce service** that manages inventory and orders. It demonstrates how Flamingock coordinates multiple target systems in lockstep using the **Change-as-Code** approach. |
4243
> 🚀 **New examples will be added regularly!** Stay tuned for updates as we expand the repository to cover even more
4344
> systems and frameworks.
4445

inventory-orders-service/.sdkmanrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
java=17.0.2-open

inventory-orders-service/README.md

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
<p align="center">
2+
<img src="../misc/logo-with-text.png" width="420px" alt="Flamingock logo" />
3+
</p>
4+
___
5+
6+
# Inventory & Orders Service Example
7+
8+
## Example Overview
9+
10+
This example simulates an **e-commerce service** that manages inventory and orders. It demonstrates how Flamingock coordinates multiple target systems in lockstep using the **Change-as-Code** approach.
11+
12+
The story begins when the **marketing team** launches a promotional campaign that requires support for **discount codes**.
13+
To implement this feature safely, the product and engineering teams plan a sequence of deployments, each introducing incremental changes in a controlled, auditable way.
14+
15+
As development progresses, the **sales team** also requests the ability to quickly search and report on orders by discount code.
16+
This leads the engineers to add an index on the new field as part of the rollout, ensuring the system remains both functional and performant.
17+
18+
### Lifecycle of the feature rollout
19+
20+
1. **Initial deployment** <a id="initial-deployment"></a>
21+
*Business driver:* prepare the system for discounts while keeping the feature hidden.
22+
- Add the `discountCode` field to the `orders` collection in **MongoDB**.
23+
- Update the `OrderCreated` schema in **Kafka** to include the same field.
24+
- Create feature flags for discount functionality via **LaunchDarkly Management API**.
25+
- Deploy application code that can *handle* the new field and flag, but does not yet *use* them.
26+
- **Associated Flamingock changes:**
27+
- [`AddDiscountCodeFieldToOrders`](#adddiscountcodefieldtoorders)
28+
- [`UpdateOrderCreatedSchema`](#updateordercreatedschema)
29+
- [`AddFeatureFlagDiscounts`](#addfeatureflagdiscounts)
30+
31+
2. **Second deployment** <a id="second-deployment"></a>
32+
*Business driver:* ensure existing and new orders remain consistent.
33+
- Backfill existing orders with a default `discountCode` (e.g. `"NONE"`).
34+
- Application logic begins to populate the field for new orders, still hidden behind the flag.
35+
- Add an index on the `discountCode` field for efficient reporting queries, requested by the sales team.
36+
- **Associated Flamingock changes:**
37+
- [`BackfillDiscountsForExistingOrders`](#backfilldiscountsforexistingorders)
38+
- [`AddIndexOnDiscountCode`](#addindexondiscountcode)
39+
40+
3. **Runtime activation (no deployment)** <a id="runtime-activation"></a>
41+
*Business driver:* marketing activates discounts for customers.
42+
- The feature flag is enabled at runtime using a feature-flag tool (e.g. Unleash, LaunchDarkly).
43+
- No redeployment is required — the system is already prepared.
44+
45+
4. **Final deployment** <a id="final-deployment"></a>
46+
*Business driver:* make the discounts feature permanent and clean up temporary scaffolding.
47+
- Archive temporary feature flags via LaunchDarkly Management API using Flamingock.
48+
- Remove the conditional `if (flag)` logic from the application code.
49+
- The discounts feature is now permanent and the system has been fully cleaned up.
50+
- **Associated Flamingock changes:**
51+
- [`CleanupFeatureFlagDiscounts`](#cleanupfeatureflagdiscounts)
52+
- [`CleanupOldSchemaVersion`](#cleanupoldschemaversion)
53+
54+
### What this demonstrates
55+
56+
This example showcases Flamingock’s ability to:
57+
- Introduce, evolve, and later clean up **multiple target systems** (databases, event schemas, and configuration files).
58+
- Support the **realistic lifecycle of a feature rollout**, spanning multiple deployments.
59+
- Keep system evolution **controlled, auditable, and aligned with application code changes**.
60+
61+
---
62+
63+
## Table of Contents
64+
65+
- [Target Systems](#target-systems)
66+
- [Complementary Stack](#complementary-stack)
67+
- [Prerequisites](#prerequisites)
68+
- [Dependencies](#dependencies)
69+
- [How to Run this Example](#how-to-run-this-example)
70+
- [Proven Functionalities](#proven-functionalities)
71+
- [Implemented Changes](#implemented-changes)
72+
- [Contributing](#contributing)
73+
- [Get Involved](#get-involved)
74+
- [License](#license)
75+
76+
---
77+
78+
## Target Systems
79+
80+
This example coordinates changes across three different target systems:
81+
82+
1. **MongoDB** - Orders collection (also used as AuditStore)
83+
2. **Kafka + Schema Registry** - Event schemas for order events
84+
3. **LaunchDarkly Management API** - Feature flag creation/deletion via REST API
85+
86+
## Complementary Stack
87+
88+
- Java 17+ (required)
89+
- Gradle (wrapper included)
90+
- Docker Compose (to run MongoDB, Kafka, and Schema Registry locally)
91+
- IntelliJ IDEA (recommended IDE with full support)
92+
93+
## Prerequisites
94+
95+
Before running this example, ensure you have installed:
96+
- **Java 17 or higher** (required - this project uses Java 17 features)
97+
- Docker and Docker Compose
98+
- Git
99+
100+
For IntelliJ IDEA users: The project includes IntelliJ-specific configurations for optimal development experience.
101+
102+
## Dependencies
103+
104+
### Flamingock dependencies
105+
```kotlin
106+
implementation(platform("io.flamingock:flamingock-community-bom:$flamingockVersion"))
107+
implementation("io.flamingock:flamingock-community")
108+
annotationProcessor("io.flamingock:flamingock-processor:$flamingockVersion")
109+
```
110+
111+
### Other key dependencies
112+
```kotlin
113+
// MongoDB
114+
implementation("org.mongodb:mongodb-driver-sync:3.7.0")
115+
116+
// Kafka & Schema Registry
117+
implementation("org.apache.kafka:kafka-clients:3.7.0")
118+
implementation("io.confluent:kafka-schema-registry-client:7.5.0")
119+
implementation("org.apache.avro:avro:1.11.3")
120+
121+
// HTTP client for LaunchDarkly Management API
122+
implementation("com.squareup.okhttp3:okhttp:4.12.0")
123+
124+
// YAML config management
125+
implementation("org.yaml:snakeyaml:2.2")
126+
```
127+
128+
Check out the [compatibility documentation](https://docs.flamingock.io) for using Flamingock with MongoDB and Kafka.
129+
130+
## How to Run this Example
131+
132+
### Feature Flag Workflow
133+
134+
**Important**: This example demonstrates Flamingock's role in the feature flag lifecycle:
135+
1. **Flamingock creates** the flag structure (disabled by default) for safe deployment
136+
2. **Teams manage** the flag's runtime state through their feature flag tool (LaunchDarkly, Unleash, etc.)
137+
3. **Flamingock removes** the flag when the feature becomes permanent (same commit as code cleanup)
138+
139+
### Option 1: Run the Application (Recommended)
140+
141+
1. **Clone the Flamingock examples repository:**
142+
```bash
143+
git clone https://github.com/flamingock/flamingock-java-examples.git
144+
cd flamingock-java-examples/inventory-orders-service
145+
```
146+
147+
2. **Start the infrastructure with Docker Compose:**
148+
```bash
149+
docker-compose up -d
150+
```
151+
152+
This starts:
153+
- MongoDB on port 27017
154+
- Kafka on port 9092
155+
- Zookeeper on port 2181
156+
- Schema Registry on port 8081
157+
- LaunchDarkly mock server on port 8765
158+
159+
**Wait for all services to be healthy (this may take 1-2 minutes):**
160+
```bash
161+
# Check service health
162+
docker-compose ps
163+
164+
# Wait for Schema Registry to be ready
165+
while ! curl -f http://localhost:8081/subjects 2>/dev/null; do
166+
echo "Waiting for Schema Registry to start..."
167+
sleep 5
168+
done
169+
echo "✅ Schema Registry is ready!"
170+
171+
# Wait for LaunchDarkly mock server to be ready
172+
while ! curl -f http://localhost:8765/status 2>/dev/null; do
173+
echo "Waiting for LaunchDarkly mock server to start..."
174+
sleep 5
175+
done
176+
echo "✅ LaunchDarkly mock server is ready!"
177+
```
178+
179+
3. **Run the Flamingock migrations:**
180+
```bash
181+
./gradlew run
182+
```
183+
184+
4. **Verify the results:**
185+
186+
Check MongoDB for the orders with discount fields:
187+
```bash
188+
docker exec -it inventory-mongodb mongosh inventory --eval 'db.orders.find().pretty()'
189+
```
190+
191+
Check Schema Registry for the evolved schemas:
192+
```bash
193+
curl http://localhost:8081/subjects
194+
curl http://localhost:8081/subjects/order-created-value/versions
195+
```
196+
197+
Check the audit logs in MongoDB:
198+
```bash
199+
docker exec -it inventory-mongodb mongosh inventory --eval 'db.flamingockAuditLogs.find().pretty()'
200+
```
201+
202+
5. **Clean up when done:**
203+
```bash
204+
docker-compose down -v
205+
```
206+
207+
### Option 2: Run Tests
208+
209+
Run the integration tests with Testcontainers (no Docker Compose needed):
210+
```bash
211+
./gradlew test
212+
```
213+
214+
## Troubleshooting
215+
216+
### Schema Registry Connection Issues
217+
218+
If you see connection errors to port 8081:
219+
220+
1. **Check if all services are healthy:**
221+
```bash
222+
docker-compose ps
223+
```
224+
All services should show "healthy" status.
225+
226+
2. **Check Schema Registry logs:**
227+
```bash
228+
docker logs inventory-schema-registry
229+
```
230+
231+
3. **Restart services if needed:**
232+
```bash
233+
docker-compose down
234+
docker-compose up -d
235+
```
236+
237+
4. **Manual health check:**
238+
```bash
239+
# Test each service individually
240+
curl http://localhost:8081/subjects # Schema Registry
241+
curl http://localhost:8765/status # LaunchDarkly mock server
242+
nc -zv localhost 9092 # Kafka
243+
nc -zv localhost 27017 # MongoDB
244+
```
245+
246+
### Common Issues
247+
248+
- **Schema Registry takes time to start**: Wait 1-2 minutes for full startup
249+
- **Port conflicts**: Ensure ports 27017, 9092, 2181, 8081, 8765 are available
250+
- **Docker resources**: Ensure Docker has sufficient memory (recommend 4GB+)
251+
252+
## Proven Functionalities
253+
254+
This example demonstrates the following Flamingock capabilities:
255+
256+
**Multi-Target System Configuration** - Coordinating changes across MongoDB, Kafka, and LaunchDarkly Management API
257+
258+
**Transactional vs Non-Transactional Changes** - MongoDB changes are transactional, while Kafka and LaunchDarkly API changes are non-transactional
259+
260+
**Change-as-Code Pattern** - All system evolution is versioned and auditable through code
261+
262+
**Schema Evolution** - Backward-compatible schema changes in Kafka
263+
264+
**Feature Flag Lifecycle Management** - Creates flags for safe deployment, removes them when features become permanent
265+
266+
**Audit Trail** - Complete history of all changes stored in MongoDB
267+
268+
**Rollback Support** - Each change includes rollback logic for recovery
269+
270+
## Implemented Changes
271+
272+
273+
| Deployment Step | Change Name | Target Systems | Operation | Description |
274+
|--------------------------------|-------------------------------------------------------------------------------------|-----------------------|-----------------------------------|----------------------------------------------------------------------------------------------|
275+
| [Initial](#initial-deployment) | <a id="adddiscountcodefieldtoorders"></a>`AddDiscountCodeFieldToOrders` | MongoDB | Alter collection / add field | Adds `discountCode` (nullable) to the orders collection |
276+
| [Initial](#initial-deployment) | <a id="updateordercreatedschema"></a>`UpdateOrderCreatedSchema` | Kafka Schema Registry | Register new schema version | Publishes a new version of the OrderCreated event schema including discountCode |
277+
| [Initial](#initial-deployment) | <a id="addfeatureflagdiscounts"></a>`AddFeatureFlagDiscounts` | LaunchDarkly API | Create flags | Creates feature flags for discount functionality using LaunchDarkly Management API |
278+
| [Second](#second-deployment) | <a id="backfilldiscountsforexistingorders"></a>`BackfillDiscountsForExistingOrders` | MongoDB | Update | Updates existing orders with discountCode = "NONE" |
279+
| [Second](#second-deployment) | <a id="addindexondiscountcode"></a>`AddIndexOnDiscountCode` | MongoDB | Create index | Creates an index on discountCode to support reporting and efficient lookups |
280+
| [Final](#final-deployment) | <a id="cleanupfeatureflagdiscounts"></a>`CleanupFeatureFlagDiscounts` | LaunchDarkly API | Archive flags | Archives temporary feature flags once the feature is permanent and code guards are removed |
281+
| [Final](#final-deployment) | <a id="cleanupoldschemaversion"></a>`CleanupOldSchemaVersion` | Kafka Schema Registry | Disable/delete old schema version | Removes outdated schema once all consumers have migrated to the new version |
282+
283+
## Example Output
284+
285+
After running the migrations, you'll see:
286+
- Orders in MongoDB with discount fields populated
287+
- Two schema versions in Schema Registry (V1 and V2)
288+
- LaunchDarkly Management API calls for feature flag creation/archival via mock server
289+
- Complete audit trail in the flamingockAuditLogs collection
290+
291+
## Architecture Notes
292+
293+
The example uses:
294+
- **MongoDB** as both a target system and the audit store
295+
- **NonTransactionalTargetSystem** for Kafka and LaunchDarkly API changes
296+
- **Utility classes** (KafkaSchemaManager, LaunchDarklyClient) for clean API abstractions
297+
- **Staged execution** - All changes run in sequence to ensure consistency
298+
- **HTTP REST calls** to LaunchDarkly Management API showing real integration patterns
299+
300+
---
301+
302+
## Contributing
303+
304+
We welcome contributions! If you have an idea for a new example or improvement to an existing one, feel free to submit a pull request. Check out our [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines.
305+
306+
---
307+
308+
## Get Involved
309+
310+
⭐ Star the [Flamingock repository](https://github.com/flamingock/flamingock-java) to show your support!
311+
312+
🐞 Report issues or suggest features in the [Flamingock issue tracker](https://github.com/flamingock/flamingock-java/issues).
313+
314+
💬 Join the discussion in the [Flamingock community](https://github.com/flamingock/flamingock-java/discussions).
315+
316+
---
317+
318+
## License
319+
320+
This repository is licensed under the [Apache License 2.0](../LICENSE.md).
321+
322+
---
323+
324+
## Explore, experiment, and empower your projects with Flamingock!
325+
326+
Let us know what you think or where you'd like to see Flamingock used next.

0 commit comments

Comments
 (0)