|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +**flamingock-java-template-mongodb** is a Flamingock template library that provides declarative, YAML-based MongoDB operations. It enables "no-code" system evolution by allowing developers to define MongoDB changes in YAML files instead of writing Java classes. |
| 8 | + |
| 9 | +This is a **standalone repository** — it is not a module of `flamingock-java`. It depends on Flamingock core artifacts published to Maven Local or Maven Central. |
| 10 | + |
| 11 | +### What is Flamingock? |
| 12 | + |
| 13 | +Flamingock is a Change-as-Code (CaC) platform for audited, synchronized evolution of distributed systems. It applies versioned, auditable changes to external systems (databases, queues, configs, schemas, etc.) at application startup, in lockstep with the application lifecycle. |
| 14 | + |
| 15 | +- **Not** a database migration tool (like Flyway or Liquibase) |
| 16 | +- **Not** a CI/CD tool or infra-as-code (like Terraform) |
| 17 | +- Changes are called "changes", never "migrations" |
| 18 | + |
| 19 | +### How This Template Fits |
| 20 | + |
| 21 | +Flamingock supports two authoring modes: |
| 22 | +1. **Programmatic** — Java classes annotated with `@Change`, `@Apply`, `@Rollback` |
| 23 | +2. **Declarative (Templates)** — YAML files processed by template implementations |
| 24 | + |
| 25 | +This module implements option 2 for MongoDB. Users write YAML like: |
| 26 | + |
| 27 | +```yaml |
| 28 | +type: insert |
| 29 | +collection: users |
| 30 | +parameters: |
| 31 | + documents: |
| 32 | + - { name: "Alice", role: "admin" } |
| 33 | +``` |
| 34 | +
|
| 35 | +The template parses it into `MongoOperation` POJOs, validates them, and executes against a `MongoDatabase`. |
| 36 | + |
| 37 | +## Build System |
| 38 | + |
| 39 | +Standalone Gradle project with Kotlin DSL. Java 8 target. |
| 40 | + |
| 41 | +### Common Commands |
| 42 | + |
| 43 | +```bash |
| 44 | +# Build |
| 45 | +./gradlew build |
| 46 | +
|
| 47 | +# Run all tests (requires Docker for TestContainers MongoDB) |
| 48 | +./gradlew test |
| 49 | +
|
| 50 | +# Check license headers |
| 51 | +./gradlew spotlessCheck |
| 52 | +
|
| 53 | +# Fix license headers |
| 54 | +./gradlew spotlessApply |
| 55 | +
|
| 56 | +# Publish to local Maven repo (for flamingock-java to consume) |
| 57 | +./gradlew publishToMavenLocal |
| 58 | +``` |
| 59 | + |
| 60 | +### Dependencies |
| 61 | + |
| 62 | +- **Flamingock core artifacts** (`flamingock-core-commons`, `flamingock-processor`, etc.) at version defined by `flamingockVersion` in `build.gradle.kts` |
| 63 | +- **MongoDB driver** `mongodb-driver-sync:4.0.0` — `compileOnly` (user provides at runtime) |
| 64 | +- **TestContainers** for integration tests (requires Docker) |
| 65 | +- Resolves from `mavenLocal()` first, then `mavenCentral()` |
| 66 | + |
| 67 | +### Flamingock Core Dependency |
| 68 | + |
| 69 | +When working on this project alongside `flamingock-java`, you must first publish core artifacts locally: |
| 70 | + |
| 71 | +```bash |
| 72 | +cd /path/to/flamingock-java |
| 73 | +./gradlew publishToMavenLocal |
| 74 | +``` |
| 75 | + |
| 76 | +Then this project picks them up via `mavenLocal()`. Keep versions in sync between both projects. |
| 77 | + |
| 78 | +## Architecture |
| 79 | + |
| 80 | +### Execution Flow |
| 81 | + |
| 82 | +``` |
| 83 | +YAML → MongoOperation (deserialized) → MongoOperationValidator → MongoOperationType (enum factory) → MongoOperator subclass → MongoDB Driver |
| 84 | +``` |
| 85 | +
|
| 86 | +### Key Classes |
| 87 | +
|
| 88 | +| Class | Role | |
| 89 | +|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 90 | +| `MongoChangeTemplate` | Entry point. Extends `AbstractChangeTemplate<Void, MongoOperation, MongoOperation>`. Annotated `@ChangeTemplate(name = "mongodb-sync-template", multiStep = true)` | |
| 91 | +| `MongoOperation` | POJO model deserialized from YAML. Fields: `type`, `collection`, `parameters` | |
| 92 | +| `MongoOperationType` | Enum with 11 operations + factory pattern via `BiFunction` | |
| 93 | +| `MongoOperationValidator` | Pre-execution validation. Collects all errors before throwing | |
| 94 | +| `MongoOperator` | Abstract base for operators. Template method: `apply()` → `applyInternal()` | |
| 95 | +| `ValidationError` | Structured error with entityId/entityType/message | |
| 96 | +
|
| 97 | +### Supported Operations (11) |
| 98 | +
|
| 99 | +| Operation | Transactional | Key Parameters | |
| 100 | +|--------------------|:-------------:|----------------------------------------------------| |
| 101 | +| `createCollection` | No | collection name only | |
| 102 | +| `dropCollection` | No | collection name only | |
| 103 | +| `renameCollection` | No | `target` | |
| 104 | +| `modifyCollection` | No | `validator`, `validationLevel`, `validationAction` | |
| 105 | +| `createIndex` | No | `keys`, `options` | |
| 106 | +| `dropIndex` | No | `indexName` or `keys` | |
| 107 | +| `insert` | Yes | `documents`, `options` | |
| 108 | +| `update` | Yes | `filter`, `update`, `multi`, `options` | |
| 109 | +| `delete` | Yes | `filter` | |
| 110 | +| `createView` | No | `viewOn`, `pipeline` | |
| 111 | +| `dropView` | No | collection name only | |
| 112 | +
|
| 113 | +### Package Structure |
| 114 | +
|
| 115 | +``` |
| 116 | +io.flamingock.template.mongodb |
| 117 | +├── MongoChangeTemplate.java # Template entry point |
| 118 | +├── mapper/ # YAML Map → MongoDB Options conversion |
| 119 | +│ ├── IndexOptionsMapper |
| 120 | +│ ├── InsertOptionsMapper |
| 121 | +│ ├── UpdateOptionsMapper |
| 122 | +│ ├── CreateViewOptionsMapper |
| 123 | +│ ├── RenameCollectionOptionsMapper |
| 124 | +│ └── MapperUtil # Type-safe extraction utilities |
| 125 | +├── model/ |
| 126 | +│ ├── MongoOperation # YAML payload POJO |
| 127 | +│ ├── MongoOperationType # Enum factory (11 types) |
| 128 | +│ └── operator/ # 11 MongoOperator subclasses |
| 129 | +└── validation/ |
| 130 | + ├── MongoOperationValidator # Comprehensive validation |
| 131 | + ├── ValidationError # Error model |
| 132 | + └── MongoTemplateValidationException # Exception wrapper |
| 133 | +``` |
| 134 | +
|
| 135 | +### TemplatePayload and Two-Phase Validation |
| 136 | +
|
| 137 | +`TemplatePayload` is the contract that all APPLY and ROLLBACK generic types in `ChangeTemplate<CONFIG, APPLY, ROLLBACK>` must implement. It has a single method: `validate()` returning `List<TemplatePayloadValidationError>`. |
| 138 | +
|
| 139 | +**Motivation — early validation at load time:** |
| 140 | +
|
| 141 | +Flamingock's pipeline lifecycle has two distinct phases: **load time** (YAML parsed → typed objects built) and **execution time** (template methods run against real systems). `TemplatePayload.validate()` enables structural validation at load time, so a malformed YAML change at step 50 is caught before steps 1-49 execute. |
| 142 | +
|
| 143 | +The framework calls `validate()` on every payload during the loaded-change validation phase (`SimpleTemplateLoadedChange.validateApplyPayload()` / `MultiStepTemplateLoadedChange.validateApplyPayload()`), collecting all errors before any change runs. |
| 144 | +
|
| 145 | +**How it works in this project:** |
| 146 | +
|
| 147 | +`MongoOperation implements TemplatePayload`. Its `validate()` method is the hook for the framework to validate each operation's structure (type, collection, parameters) at load time. |
| 148 | +
|
| 149 | +Currently `validate()` returns an empty list — the existing validation lives in `MongoOperationValidator` and runs at execution time inside `MongoChangeTemplate.apply()`. The next task is to **move structural validation into `MongoOperation.validate()`** so the framework catches errors early. `MongoOperationValidator` may still handle execution-time concerns, but the structural checks (missing type, invalid collection name, missing required parameters) belong in `validate()`. |
| 150 | +
|
| 151 | +**Type bound:** `ChangeTemplate` enforces `APPLY_FIELD extends TemplatePayload` at the generic level — you cannot create a template with payload types that don't implement it. |
| 152 | +
|
| 153 | +**Built-in implementations in flamingock-java:** |
| 154 | +- `TemplateString` — wraps `String` payloads (used by `SqlTemplate`), validates non-null/non-blank |
| 155 | +- Test-only stubs that return empty error lists |
| 156 | +
|
| 157 | +### SPI Registration |
| 158 | +
|
| 159 | +Discovered automatically via Java ServiceLoader: |
| 160 | +``` |
| 161 | +META-INF/services/io.flamingock.api.template.ChangeTemplate |
| 162 | +→ io.flamingock.template.mongodb.MongoChangeTemplate |
| 163 | +``` |
| 164 | +
|
| 165 | +## Testing |
| 166 | +
|
| 167 | +### Test Categories |
| 168 | +
|
| 169 | +| Category | Location | Requires Docker | |
| 170 | +|----------|----------|:---:| |
| 171 | +| Integration (full pipeline) | `MongoChangeTemplateTest` | Yes | |
| 172 | +| Operator tests (1 per operation) | `operations/` | Yes | |
| 173 | +| Multi-operation tests | `operations/MultipleOperationsTest` | Yes | |
| 174 | +| Validator unit tests (~38) | `validation/MongoOperationValidatorTest` | No | |
| 175 | +| Mapper unit tests | `mapper/*Test` | No | |
| 176 | +
|
| 177 | +### Test Resources |
| 178 | +
|
| 179 | +- `src/test/resources/flamingock/pipeline.yaml` — test pipeline configuration |
| 180 | +- Test change YAML files in `src/test/java/io/flamingock/template/mongodb/changes/` |
| 181 | +
|
| 182 | +### Running Tests |
| 183 | +
|
| 184 | +All integration tests use TestContainers with MongoDB, so **Docker must be running**. |
| 185 | +
|
| 186 | +```bash |
| 187 | +# All tests |
| 188 | +./gradlew test |
| 189 | +
|
| 190 | +# Only unit tests (no Docker needed) |
| 191 | +./gradlew test --tests "io.flamingock.template.mongodb.validation.*" --tests "io.flamingock.template.mongodb.mapper.*" |
| 192 | +
|
| 193 | +# Specific operator test |
| 194 | +./gradlew test --tests "io.flamingock.template.mongodb.operations.InsertOperatorTest" |
| 195 | +``` |
| 196 | + |
| 197 | +## Known Issues |
| 198 | + |
| 199 | +See `ANALYSIS.md` for a detailed quality assessment. Key issues: |
| 200 | + |
| 201 | +1. **Rollback path skips validation** — `MongoChangeTemplate.rollback()` doesn't call `MongoOperationValidator.validate()` before executing |
| 202 | +2. **InsertOperator silently swallows null/empty documents** — redundant guard bypasses validation |
| 203 | +3. **modifyCollection has no parameter validation** — validator returns empty error list |
| 204 | +4. **Type-unsafe parameter extraction** — `@SuppressWarnings("unchecked")` raw casts in `MongoOperation` getters |
| 205 | +5. **CreateIndexOperator claims transactional but ignores session** — misleading behavior |
| 206 | +6. **Collation mapping broken for YAML input** — `MapperUtil.getCollation()` expects `Collation` object, gets `Map` |
| 207 | + |
| 208 | +## Terminology |
| 209 | + |
| 210 | +- Use "**changes**", not "migrations" |
| 211 | +- Use "**system evolution**", not "database migration" |
| 212 | +- The template name is `mongodb-sync-template` (as declared in `@ChangeTemplate` annotation) |
| 213 | + |
| 214 | +## License |
| 215 | + |
| 216 | +Apache License 2.0. All source files must include the Flamingock license header. Use `./gradlew spotlessApply` to add missing headers. |
0 commit comments