Skip to content

Keep the event log of each Bounded Context in its own Datastore kind - #206

Merged
alexander-yevsyukov merged 13 commits into
masterfrom
event-store-context-prefix
Aug 28, 2026
Merged

Keep the event log of each Bounded Context in its own Datastore kind#206
alexander-yevsyukov merged 13 commits into
masterfrom
event-store-context-prefix

Conversation

@alexander-yevsyukov

Copy link
Copy Markdown
Contributor

This PR is the gcloud-jvm part of keeping the event log of each Bounded Context
in its own physical storage. The core part is core-jvm PR
#1673; the RDBMS part is
jdbc-storage PR #182.

What changed

Dependency bump — the behavioral pivot. CoreJvm moves to 2.0.0-SNAPSHOT.540,
where DefaultEventStore creates its record storage under a StorageGroup named
after the context. This library already keys grouped storages by
(group, record type), so per-context kinds — Billing-Event, Shipping-Event,
Billing_System-Event — appear through the existing grouped-layout path with no
storage-code changes
.

Context-addressed layout registration. Grouped layouts could be registered only
by entity state type, so the layout of a context-grouped storage could not be
customized. Two new overloads close that gap, deriving the group through
StorageGroup.of(BoundedContextName) — the same single source of truth core-jvm
uses:

  • DatastoreStorageFactory.Builder.organizeRecords(BoundedContextName, Class, RecordLayout)
  • RecordLayouts.Builder.add(BoundedContextName, Class, RecordLayout)

Why this repo needs less than jdbc-storage. Datastore kinds are arbitrary
UTF-8, stored verbatim and compared case-sensitively, and Kind.of(recordType, group)
composes <group>-<SimpleName> with no character replacement or truncation. So the
sanitization and clash detection that jdbc-storage needed have no counterpart here
— the composition is already injective for distinct group names, and the dash cannot
occur in a Proto type name, keeping grouped kinds apart from type-derived ones. This
is verified rather than assumed: EventLogIsolationSpec pins that contexts differing
only in case keep separate event logs at the storage level — the deliberate mirror of
jdbc-storage's test asserting the opposite for RDBMS tables.

Tests (355 tests, 0 failures, 18 Docker-gated skips):

  • KindSpec — grouped-kind composition, the context-derived group, verbatim
    case-sensitive group names;
  • RecordLayoutsSpec — a context-registered layout is honored; an unregistered
    context defaults to the flat grouped-kind layout;
  • EventLogIsolationSpec — two contexts over one factory keep isolated event logs,
    including the case-different pair;
  • DatastoreDefaultEventStoreTest — core's DefaultEventStoreTest contract against
    the local Datastore emulator, with data wiped per test;
  • DatastoreStorageFactoryBuilderTest gains the NullPointerTester default for the
    new BoundedContextName parameter.

Documentation. The new docs/event-log-migration.md guides moving the stored
events from the shared spine.core.Event kind to the per-context kinds by the type
property, iterating namespaces for multi-tenant deployments, and covers the
custom-kind, shared-event-type, and composite-index cases. Linked from README.md.

Housekeeping (separate commits): ./config/pull; the root build script adapted to
the updated config API (JacksonV2 forcing groups, Caffeine,
coreJvmCompiler.gradlePlugin, the retired monolithic tool-base entry dropped) plus
KotlinPoet forcing that the KSP processor classpath now needs; the local dependency
refresh (CoreJvmCompiler .091, Logging .424, Validation .463); and the
regenerated dependency reports.

Notes for reviewers

  • The task plan with the full rationale is in
    .agents/tasks/event-store-context-prefix.md.
  • Existing deployments are unaffected until they upgrade; on upgrade, historical
    events stay under spine.core.Event until migrated — see the new docs page.
  • Single-type organizeRecords(Event.class, ...) and useRecordStorage(...)
    registrations never serve grouped storages (pre-existing, documented behavior),
    so they no longer reach the event store. The migration page says so.

🤖 Generated with Claude Code

alexander-yevsyukov and others added 12 commits August 27, 2026 18:39
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The version brings the event store storage grouped by the Bounded
Context (core-jvm PR #1673), which lands each context's event log
under its own kind, e.g. `Billing-Event`, through the existing
grouped-layout path.

The test fixtures of `spine-server` join the test classpath via the
`server-test-fixtures` capability, providing the `DefaultEventStoreTest`
contract suite. The main `spine-server` classes arrive transitively:
the published fixtures variant depends on its own main component.
The layouts of grouped storages were registered by the entity state
type only, so the layout of a context-grouped storage — the event store
of a Bounded Context — could not be customized. The new
`organizeRecords(BoundedContextName, Class, RecordLayout)` overload and
its `RecordLayouts.Builder` counterpart address such a storage by the
context name, deriving the group via `StorageGroup.of(BoundedContextName)`
— the same single source of truth `core-jvm` uses.
 - `KindSpec` — grouped-kind composition, the context-derived group,
   and the verbatim, case-sensitive group names.
 - `RecordLayoutsSpec` — a context-registered layout is honored;
   an unregistered context defaults to the flat grouped-kind layout.
 - `EventLogIsolationSpec` — two contexts over one factory keep
   isolated event logs, including the contexts differing only in case:
   Datastore kinds are stored verbatim, the opposite of the RDBMS
   behavior pinned in `jdbc-storage`.
 - `DatastoreDefaultEventStoreTest` — the `DefaultEventStoreTest`
   contract of `core-jvm` against the local Datastore emulator, with
   the emulator data wiped per test.

`DatastoreStorageFactoryBuilderTest` gains the `NullPointerTester`
default for the `BoundedContextName` parameter of the new overload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The new `docs/event-log-migration.md` guides moving the stored events
from the shared `spine.core.Event` kind to the per-context kinds by
the `type` property, iterating namespaces for multi-tenant deployments,
and covers the custom-kind and shared-event-type cases. Linked from
`README.md`. The task plan records the engine-semantics reasoning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The refreshed `config` splits Jackson forcing into `JacksonV2` groups,
forces Caffeine explicitly, renames `CoreJvmCompiler.pluginLib` to
`gradlePlugin`, and retires the monolithic `tool-base` artifact.
The project-scope forcing also gains `KotlinPoet`, aligning the KSP
processor classpath the way the sibling repositories do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The local dependencies move to the latest published versions:
`CoreJvm` to `2.0.0-SNAPSHOT.540` — restoring the branch requirement
after the `config` pull — `CoreJvmCompiler` to `.091`, `Logging` to
`.424`, and `Validation` to `.463`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`DsEventStoreTest` already runs the `DefaultEventStoreTest` contract of
`core-jvm` against the emulator, wiping the data between the tests via
`@AfterEach`. The Kotlin suite added earlier on this branch repeated the
same contract for no added coverage, and its premise — that only the
constructor runs ahead of the base setup — was wrong: cleaning after
each test is equivalent to cleaning before it.

Also narrows the display name of `KindSpec`, which collided with the one
of `KindTest`, and drops the erased type arguments from a
`shouldBeInstanceOf` check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The new `organizeRecords(BoundedContextName, ..)` overload of the factory
builder was covered only indirectly: through `RecordLayouts` directly, and
through the default grouped kind. `EventLogIsolationSpec` now builds a
customized factory the way the Javadoc and the migration guide show, and
asserts that the event store of the registered context takes the custom
kind while another context keeps the derived one.

Also documents the context-addressed flavor in the `RecordLayouts` class
docs, aligns the `@MonotonicNonNull` builder fields with the initializer
style used elsewhere in the repository, and notes why the `JacksonV2`
groups are referenced by their qualified names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 27, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates gcloud-jvm to support per–Bounded Context event-log isolation in Google Cloud Datastore (i.e., each context’s Event records land in a distinct kind like Billing-Event), aligning with the corresponding core-jvm behavior change. It also adds context-addressed record-layout registration and updates build tooling and dependency metadata accordingly.

Changes:

  • Add context-addressed overloads for Datastore record layout registration and tests validating per-context kinds and isolation.
  • Add migration documentation for moving historical events from the shared spine.core.Event kind to per-context kinds, and link it from the README.
  • Update dependency/tooling infrastructure (BuildSrc POM generator improvements, dependency versions, generated dependency reports, Gradle wrapper).

Reviewed changes

Copilot reviewed 36 out of 39 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
version.gradle.kts Bump published snapshot version.
README.md Add a pointer to the event-log migration guide.
gradle/wrapper/gradle-wrapper.properties Update Gradle wrapper distribution URL.
docs/event-log-migration.md New migration guide describing how to move historical events to per-context kinds.
docs/dependencies/pom.xml Update documented dependency versions and add new plugin dependency entry.
docs/dependencies/dependencies.md Regenerated dependency/license report for updated versions.
datastore/src/test/kotlin/io/spine/server/storage/datastore/record/EventLogIsolationSpec.kt New emulator-backed test asserting per-context event log isolation (including case-different context names).
datastore/src/test/kotlin/io/spine/server/storage/datastore/KindSpec.kt New tests for grouped-kind composition behavior.
datastore/src/test/kotlin/io/spine/server/storage/datastore/config/RecordLayoutsSpec.kt New tests for context-grouped layout registration and defaults.
datastore/src/test/java/io/spine/server/storage/datastore/DatastoreStorageFactoryBuilderTest.java Extend nullability testing defaults for new BoundedContextName parameter.
datastore/src/main/java/io/spine/server/storage/datastore/DatastoreStorageFactory.java Add organizeRecords(BoundedContextName, Class, RecordLayout) overload and refine nullness annotations/docs.
datastore/src/main/java/io/spine/server/storage/datastore/config/RecordLayouts.java Add RecordLayouts.Builder.add(BoundedContextName, Class, RecordLayout) for context-grouped storages.
datastore/build.gradle.kts Adjust test dependency to explicitly require server-test-fixtures capability.
buildSrc/src/test/kotlin/io/spine/gradle/report/pom/PomGeneratorIgTest.kt New Gradle TestKit integration test validating stable/accurate generated POM output.
buildSrc/src/test/kotlin/io/spine/gradle/report/pom/DependencyWriterSpec.kt Update tests to feed resolved versions via the new collection mechanism; add coverage for unresolvable configs.
buildSrc/src/main/kotlin/io/spine/gradle/report/pom/ResolvedVersions.kt New per-project task + helper logic to collect resolved dependency versions safely across projects.
buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomXmlWriter.kt Wire resolved-versions provider into POM writing.
buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt Register per-project version collectors and make generatePom depend on them.
buildSrc/src/main/kotlin/io/spine/gradle/report/pom/DependencyWriter.kt Refactor dependency collection to consume externally-provided resolved versions; expose moduleKey for reuse.
buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt Bump Validation version and remove unused artifact constants.
buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt Update ToolBase coordinates and model the split of the former monolithic artifact into focused modules.
buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt Bump ProtoTap version.
buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt Bump Logging version.
buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt Update CoreJvm Compiler coordinates (separate Gradle plugin vs compiler plugins) and bump version.
buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt Bump CoreJvm (server) version to the behavioral pivot.
buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt Update compiler artifact naming (fat CLI distribution) and bump version.
buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt Bump Base Libraries version.
buildSrc/src/main/kotlin/io/spine/dependency/lib/PalantirJavaFormat.kt Bump Palantir Java Format version.
buildSrc/src/main/kotlin/io/spine/dependency/lib/Log4j2.kt Bump Log4j2 version and add documented SLF4J 2.x bridge coordinate.
buildSrc/src/main/kotlin/io/spine/dependency/lib/JacksonV2.kt New Jackson 2.x dependency model to align transitive 2.x consumers.
buildSrc/src/main/kotlin/io/spine/dependency/lib/Jackson.kt Switch “primary” Jackson model to 3.x (tools.jackson), keep annotations on 2.x, and deprecate no-longer-published modules.
buildSrc/src/main/kotlin/io/spine/dependency/boms/Boms.kt Point optional Jackson BOM to Jackson 2.x BOM for transitive alignment.
buildSrc/build.gradle.kts Improve buildSrc test setup (TestKit dependency + pass buildSrc runtime classpath to functional tests); clarify Jackson 2.x rationale.
build.gradle.kts Update forced dependency alignment (JacksonV2 groups, KotlinPoet additions, CoreJvmCompiler plugin artifact rename, drop ToolBase monolith force).
.gitignore Clarify ignoring of .idea/kotlinc.xml to avoid IDE churn.
.github/workflows/gradle-wrapper-validation.yml Remove wrapper-validation workflow file.
.agents/tasks/event-store-context-prefix.md Add a task-plan document describing rationale/work items for this change set.
Suppressed comments (1)

docs/event-log-migration.md:77

  • This URL points to the "Datastore to Datastore Delete" template, which is for bulk deletion rather than migration/copy. It would be safer to link to Dataflow template/pipeline authoring docs (or other copy/migration docs) instead.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/event-log-migration.md Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccc0212624

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/event-log-migration.md Outdated
The guide pointed at the `datastore-to-datastore-delete` Dataflow
template as an option for copying the event entities. The template
deletes the matching entities instead of copying them, so an operator
following the recommendation could erase the historical event log
without creating the per-context entities.

The guide now states that no ready-made tool performs this copy — the
provided templates delete or move verbatim, and a managed import
restores the original kind — and points to the Datastore client library
and the Apache Beam connector for writing the copy. A warning asks for
a managed export before the migration starts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov self-assigned this Aug 27, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Aug 27, 2026
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.05%. Comparing base (f536e55) to head (3a1bf64).

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #206      +/-   ##
============================================
+ Coverage     90.97%   91.05%   +0.08%     
  Complexity      475      475              
============================================
  Files            62       62              
  Lines          1662     1677      +15     
  Branches         92       92              
============================================
+ Hits           1512     1527      +15     
  Misses          121      121              
  Partials         29       29              
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alexander-yevsyukov alexander-yevsyukov moved this from 🏗 In progress to In Review in v2.0 Aug 27, 2026
@alexander-yevsyukov
alexander-yevsyukov merged commit 13f6b45 into master Aug 28, 2026
9 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the event-store-context-prefix branch August 28, 2026 11:35
@github-project-automation github-project-automation Bot moved this from In Review to ✅ Done in v2.0 Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants