Skip to content

docs: pivot to modulith with strict service boundaries#59

Merged
phuongnse merged 2 commits into
mainfrom
docs/distributed-ready-foundation
May 22, 2026
Merged

docs: pivot to modulith with strict service boundaries#59
phuongnse merged 2 commits into
mainfrom
docs/distributed-ready-foundation

Conversation

@phuongnse

@phuongnse phuongnse commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

Captures the architectural pivot from "modular monolith that can extract later" to modulith with strict service boundaries — every module is a service contract from day 1, and extraction is a redeploy rather than a refactor. This is Phase 0 of the rollout: docs only, no production code change. Subsequent phases (broker, gRPC, per-module DB, EF migrations, deployment) follow in their own PRs.

Linked spec

Requirements & rules followed

  • Spec → code — N/A: no code change; decisions captured in ADRs that subsequent PRs implement against
  • Gate 0 — N/A: foundation pivot, no AC delivery
  • Gate 1 — N/A: docs-only PR
  • Gate 2./scripts/check-doc-drift.sh green; PROGRESS.md updated with rollout plan; ADR cross-links + ARCHITECTURE diagrams referenced where they exist
  • Gate 3 — multiple new durable rules captured (ADR-010..023); CLAUDE.md P0/P1 + playbooks/patterns.md updated to match
  • No new TODO / FIXME / placeholder / stub — implementation is intentionally deferred to subsequent PRs and tracked in PROGRESS.md's phase table, not as TODO markers

What this PR does NOT do

  • Does not introduce Kafka, Schema Registry, Vault, gRPC, or per-module DBs in code — those are Phase 1 PRs
  • Does not refactor Axis.Shared — Phase 1
  • Does not add Axis.{Module}.Contracts projects — Phase 2
  • Does not switch tests from EnsureCreated to MigrateAsync — Phase 3

The existing implementation continues to work as-is. The "Service-boundary retrofit ⏳" notes on each module in PROGRESS.md track what each module owes the new contract.

Summary by CodeRabbit

  • Documentation
    • Updated architecture to formalize modulith service boundaries, per-module databases (schema-per-tenant), and gateway/identity responsibilities
    • Added cross-module communication standards: Kafka events (Avro + CloudEvents) and gRPC sync with versioned contracts
    • Clarified tenant propagation, JWT/JWKS validation, observability, EF Core migration rules, and forbidden cross-module anti-patterns
    • Revised rollout/playbook guidance and tech-stack/roadmap details

Review Change Stack

Captures the architectural decision to make every module a service
contract from day 1, so extraction is a redeploy rather than a refactor.
This is a foundation PR — no production code change yet; subsequent PRs
in the rollout (broker, gRPC, per-module DB, EF migrations, deployment)
each follow with their own code.

- ADR-001, ADR-002, ADR-009 marked superseded; ADR-010..023 added.
- ARCHITECTURE.md rewritten: containers include Kafka, Schema Registry,
  Vault, Grafana stack; per-module DB layout; Identity as remote
  dependency.
- CLAUDE.md P0/P1 rules rewritten for strict service boundaries:
  cross-module = Kafka event or gRPC; no shared kernel impl; MediatR
  intra-module only; EnsureCreated forbidden.
- patterns.md § Cross-module communication rewritten for Kafka + gRPC
  pattern with Avro/CloudEvents example.
- PROGRESS.md tracks the 5-phase rollout (Phase 0 doc work here; Phase
  1-4 in follow-ups). Existing module ✅ states get a "Service-boundary
  retrofit ⏳" line because the implementation predates ADR-010.
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR updates five documentation files to formalize a strict modulith architecture with explicit service boundaries, replacing prior guidance. The changes establish per-module database isolation, event-driven cross-module communication via Kafka (Avro+CloudEvents), gRPC sync escape hatches, JWKS-based local JWT validation, and a phased retrofit roadmap for existing modules.

Changes

Modulith Service Boundaries & Implementation Roadmap

Layer / File(s) Summary
Modulith Architecture Foundation & System Design
CLAUDE.md, docs/ARCHITECTURE.md, docs/TECH_STACK.md
System design shifts from a shared monolith to a strict modulith with API gateway (Axis.Api), remote Identity, per-module PostgreSQL, Kafka+Schema Registry, and observability stack. Module roles standardized to Contracts/Domain/Application/Infrastructure/API entrypoint. Cross-module rules enforced: Kafka events (Avro+CloudEvents), gRPC sync only (versioned proto), JWKS-based JWT validation, no shared DbContext or in-process cross-module dispatch. ADR-010 documents extraction = redeploy contract requirements.
Data Strategy & Multi-Tenancy Model
docs/ARCHITECTURE.md, docs/TECH_STACK.md
Per-module PostgreSQL databases with schema-per-tenant isolation. Tenant resolution extracted from JWT org_id claim by gateway, propagated via gRPC metadata and CloudEvents tenantid, restored via scoped ITenantContext, and enforced through EF Core search_path interception. Per-module Wolverine outbox/envelope schemas with transaction guarantees. Kafka partitioning by organizationId. ADRs-011/012 define DB and outbox locality.
Cross-Module Communication Patterns
docs/TECH_STACK.md, docs/playbooks/patterns.md
Event-driven default: Kafka transport with Avro+CloudEvents payloads and idempotent local read-model consumption. gRPC sync API as escape-hatch when eventual consistency insufficient. Share Nothing principle (query own DB only) elevated to core P0. Anti-patterns expanded to include in-process Application service calls. End-to-end example provided (Avro contract, Wolverine outbox publish, idempotent consumer, local query). ADRs-013/014 define transports and API boundaries.
Identity, Authentication & Secrets Management
docs/ARCHITECTURE.md, docs/TECH_STACK.md
Remote Identity service handles OAuth2/OIDC; other modules validate JWTs locally via cached JWKS endpoints. Identity gRPC contract used only for sync user/role lookups. Vault-backed secrets in production; .env/user-secrets in development. Minimal API endpoints require JWT authorization. ADRs-015/016/017 define remote Identity model and service discovery; ADRs-021/023 enforce per-module EF migrations and Vault usage.
Operations, Observability & Infrastructure
docs/ARCHITECTURE.md, docs/TECH_STACK.md
OpenTelemetry tracing end-to-end (Wolverine/gRPC interceptors). Prometheus metrics with Grafana Mimir long-term storage. Serilog-to-OTEL-to-Loki structured logging (trace/tenant/module context). Health checks: /health and /health/ready. Per-module EF Core migrations in CI (no EnsureCreated). Production Vault secrets, dev .env. ADRs-018/020 cover observability and saga orchestration.
Developer Workflow Rules & CLAUDE.md Boundaries
CLAUDE.md
Tightened developer checklist and P1 rules: MediatR strictly intra-module; Wolverine outbox for cross-module events; Minimal APIs require JWT via JWKS; EF Core migrations mandatory for schema changes; explicit constraints for new cross-module RPC/events (versioned .proto first; Avro schema registration + CloudEvents envelope). Solution-tree documentation updated with module Contracts/Domain/Application/Infrastructure structure and test mirroring.
Phased Implementation Roadmap & Module Retrofits
docs/PROGRESS.md
Phase 0–4 foundation rollout with explicit pause on feature work until Phase 1–2 complete. Axis.Shared.Infrastructure narrowed to abstractions-only (ADR-017). Per-module retrofit plans: add Contracts (gRPC+Avro), expose JWKS, publish events via Kafka, migrate to per-module database, switch tests to EF migrations. Identity/E01 tenant provisioning reframed: Identity publishes OrganizationVerified to Kafka; modules subscribe and provision tenant schemas in own DBs. Phase 2 defers retry/alert/admin-verify logic.

Sequence Diagram(s)

No sequence diagrams generated: this PR is documentation-only, describing architectural principles and governance rules rather than introducing new interactive control flows or system behaviors.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~18 minutes

Possibly related PRs

  • phuong-labs/axis#57: Directly implements Wolverine durable inbox/outbox persistence for cross-module eventing as prescribed by ADRs and CLAUDE.md workflow rules in this PR.
  • phuong-labs/axis#30: Refines cross-module boundary enforcement and playbook organization; overlaps with CLAUDE.md and playbooks updates in this PR.
  • phuong-labs/axis#48: Adds CI checks for documentation governance that can validate adherence to the CLAUDE.md rules and architectural boundaries introduced here.

Poem

🐰 A Rabbit's Ode to Service Boundaries
From monolith's maze to modules clean,
Each service stands where it's seen,
With Kafka streams and schemas bright,
And JWKS guarding every right,
The boundaries firm, the path is clear— a distributed dawn draws near! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: a pivot from a modular monolith model to a modulith architecture with strict service boundaries, which is the core theme across all updated documentation files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/distributed-ready-foundation

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/playbooks/patterns.md`:
- Line 1049: The in-file anchor links pointing to the old heading id
"`#cross-module-data-pattern`" are now stale after renaming the heading to
"Cross-module communication pattern"; update all internal links in this document
that reference "`#cross-module-data-pattern`" to the new anchor (dash-cased form
of the updated heading) so they match the renamed section title (search for
occurrences of "`#cross-module-data-pattern`" and replace with the current
heading's anchor form).

In `@docs/TECH_STACK.md`:
- Line 15: Update the ADR-023 markdown link fragment so it matches the actual
heading anchor used in this document: replace occurrences of
([ADR-023](`#adr-023-per-module-ef-core-migrations-no-ensurecreated`)) with the
correct fragment that corresponds to the heading for ADR-023 (use the exact
slugified heading text used in the file), and apply the same replacement
wherever ADR-023 is linked (e.g., the instance in the Entity Framework Core row
and the other occurrence noted).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c4082c7d-8669-430b-99de-0ec821b30f79

📥 Commits

Reviewing files that changed from the base of the PR and between 72d2647 and 167bed6.

📒 Files selected for processing (5)
  • CLAUDE.md
  • docs/ARCHITECTURE.md
  • docs/PROGRESS.md
  • docs/TECH_STACK.md
  • docs/playbooks/patterns.md

Comment thread docs/playbooks/patterns.md
Comment thread docs/TECH_STACK.md Outdated
Markdown link check flagged seven broken fragments across CLAUDE.md,
ARCHITECTURE.md, PROGRESS.md, TECH_STACK.md, patterns.md,
patterns-index.md. Two root causes:

- ADR-010..023 headings used em-dashes, slashes, semicolons, and
  parentheses, so GitHub's slugifier produced anchors that did not
  match the hand-written links (e.g. the actual slug for ADR-023
  was longer than the link target).
- patterns.md renamed "Cross-module data pattern" to "Cross-module
  communication pattern" but in-file and external links still
  pointed at the old anchor.

Rewrite the affected ADR headings to use only letters, digits, and
single spaces so the slugifier is deterministic, then update every
cross-reference in one bulk pass.

@coderabbitai coderabbitai 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.

♻️ Duplicate comments (2)
docs/playbooks/patterns.md (2)

707-707: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update table display text to match the renamed section heading.

Same issue as line 37: the display text says "Cross-module data pattern" but should say "Cross-module communication pattern" to match the actual heading at line 1049.

📝 Proposed fix
-| Another module's data | [Cross-module data pattern](`#cross-module-communication-pattern`) |
+| Another module's data | [Cross-module communication pattern](`#cross-module-communication-pattern`) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/playbooks/patterns.md` at line 707, Update the table row that currently
reads "| Another module's data | [Cross-module data
pattern](`#cross-module-communication-pattern`) |" so the link text matches the
renamed heading; change the display text "Cross-module data pattern" to
"Cross-module communication pattern" (i.e., update the markdown cell containing
the linked text for the "Another module's data" row) so the visible label and
the anchor "`#cross-module-communication-pattern`" are consistent with the
heading.

37-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update TOC display text to match the renamed section heading.

The link anchor is correct (#cross-module-communication-pattern), but the display text still says "Cross-module data pattern" while the actual heading at line 1049 is "Cross-module communication pattern." Update the display text for consistency.

📝 Proposed fix
-- [Cross-module data pattern](`#cross-module-communication-pattern`) ★
+- [Cross-module communication pattern](`#cross-module-communication-pattern`) ★
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/playbooks/patterns.md` at line 37, Update the TOC entry text to match
the renamed section heading: change the display text "Cross-module data pattern"
to "Cross-module communication pattern" for the existing link that points to
"`#cross-module-communication-pattern`" so the visible label and the section
heading (the renamed heading at line 1049) are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@docs/playbooks/patterns.md`:
- Line 707: Update the table row that currently reads "| Another module's data |
[Cross-module data pattern](`#cross-module-communication-pattern`) |" so the link
text matches the renamed heading; change the display text "Cross-module data
pattern" to "Cross-module communication pattern" (i.e., update the markdown cell
containing the linked text for the "Another module's data" row) so the visible
label and the anchor "`#cross-module-communication-pattern`" are consistent with
the heading.
- Line 37: Update the TOC entry text to match the renamed section heading:
change the display text "Cross-module data pattern" to "Cross-module
communication pattern" for the existing link that points to
"`#cross-module-communication-pattern`" so the visible label and the section
heading (the renamed heading at line 1049) are consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8818ae98-7d6f-4797-bd3b-e85c4cb0ba13

📥 Commits

Reviewing files that changed from the base of the PR and between 167bed6 and d215224.

📒 Files selected for processing (6)
  • CLAUDE.md
  • docs/ARCHITECTURE.md
  • docs/PROGRESS.md
  • docs/TECH_STACK.md
  • docs/playbooks/patterns-index.md
  • docs/playbooks/patterns.md
✅ Files skipped from review due to trivial changes (3)
  • docs/playbooks/patterns-index.md
  • CLAUDE.md
  • docs/PROGRESS.md

@phuongnse phuongnse merged commit 4ed1d1b into main May 22, 2026
5 checks passed
phuongnse added a commit that referenced this pull request May 23, 2026
Same trap as PRs #57/#59 fixed before: em-dash + comma in headings
make GitHub's slugify produce unpredictable anchors. Lychee link
check caught 3 broken refs across CONTRIBUTING.md, ARCHITECTURE.md,
and TECH_STACK.md.

Heading rewritten to alphanumeric-only:
  ADR-025: Transport selection rule by message-name suffix

The lesson is in memory/feedback_github_actions_hyphen_expression.md
and the playbook entry agent-checklist.md § Chore PRs — I missed
applying it when authoring ADR-025 the first time. Anchors swept
across 8 markdown files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant