From 4e380805785c01a340c1f0b677c61916af5ebcb6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:29:49 +0000 Subject: [PATCH] docs(arch): autonomous evolution of architecture blueprints - Updated `event-driven-architecture/implementation-guide.md` to properly document idempotent consumers, transactional outbox pattern, and typed schema registries following the strict Problem-Solution cycle. - Updated `hexagonal-architecture/implementation-guide.md` to enforce domain isolation without leaking framework decorators like `@Entity`, documenting the problem and solution clearly. Co-authored-by: beginwebdev2002 <102213457+beginwebdev2002@users.noreply.github.com> --- .../implementation-guide.md | 25 +++++++++++++------ .../implementation-guide.md | 25 ++++++++++++++++--- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/architectures/event-driven-architecture/implementation-guide.md b/architectures/event-driven-architecture/implementation-guide.md index 6e7206f..7546d9b 100644 --- a/architectures/event-driven-architecture/implementation-guide.md +++ b/architectures/event-driven-architecture/implementation-guide.md @@ -90,11 +90,11 @@ class PaymentEventHandler { ### ⚠️ Problem -[Analysis of the risks] +Without tracking `eventId` locally, duplicate deliveries from the broker (due to consumer rebalancing, network drops, or retries) result in duplicate side effects. This leads to double-billing customers, duplicate orders, and compromised system integrity. ### 🚀 Solution -[Architectural justification of the solution] +Implementing an Idempotent Receiver pattern with a unique constraint on `eventId` ensures that duplicate events are safely ignored. Wrapping the deduplication check and the business logic within an atomic database transaction guarantees state consistency even under high concurrency. --- ## 2. The Transactional Outbox Pattern @@ -143,11 +143,11 @@ class OrderService { ### ⚠️ Problem -[Analysis of the risks] +The Dual-Write Anti-Pattern occurs when an application attempts to update a database and publish a message to a broker sequentially. Without distributed transactions, a failure between the two operations leaves the system in an inconsistent state (e.g., the DB is updated, but the event is never published). ### 🚀 Solution -[Architectural justification of the solution] +The Transactional Outbox pattern guarantees atomic operations. By saving the event to a local `outbox` table within the same DB transaction as the domain change, we achieve atomicity. A separate, reliable process (like a Debezium Connector or background poller) then reads the outbox table and guarantees "at-least-once" delivery to the broker. --- ## 3. Strictly Typed Schemas (Schema Registry) @@ -186,15 +186,26 @@ class OrderKafkaPublisher { ### ❌ Bad Practice -[Need to fill in example of non-optimal code] +```typescript +class OrderKafkaPublisher { + async publish(event: DomainEvent) { + // ❌ Publishing raw, untyped JSON objects + // Downstream consumers might break if 'amount' is suddenly renamed to 'totalAmount' + await this.producer.send({ + topic: 'orders.created', + messages: [{ key: event.aggregateId, value: JSON.stringify(event.payload) }] + }); + } +} +``` ### ⚠️ Problem -[Analysis of the risks] +Publishing unstructured JSON couples microservices dangerously. If the upstream service changes a field name or type, downstream consumers will crash unexpectedly, causing cascading failures and data corruption across the distributed system. ### 🚀 Solution -[Architectural justification of the solution] +A Schema Registry enforces a strict, versioned contract (e.g., Avro, Protobuf, JSON Schema) between producers and consumers. It serializes data efficiently, rejects non-compliant payloads at the producer level, and ensures backward/forward compatibility rules are respected across all teams. ---
diff --git a/architectures/hexagonal-architecture/implementation-guide.md b/architectures/hexagonal-architecture/implementation-guide.md index d4b5a24..ceda85e 100644 --- a/architectures/hexagonal-architecture/implementation-guide.md +++ b/architectures/hexagonal-architecture/implementation-guide.md @@ -170,12 +170,31 @@ export class UserController { ### ❌ Bad Practice -[Need to fill in example of non-optimal code] +```typescript +// ❌ Domain Entity leaking infrastructure concerns +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; + +@Entity('users') +export class User { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + email: string; + + @Column() + status: string; + + deactivate(): void { + this.status = 'INACTIVE'; + } +} +``` ### ⚠️ Problem -[Analysis of the risks] +Mixing framework annotations (like TypeORM `@Entity` or `@Column`) with the Core Domain Entity violates the Dependency Inversion Principle. It rigidly couples the business logic to a specific database technology. This makes the domain hard to test in isolation, impossible to migrate without rewriting core logic, and vulnerable to infrastructure-driven constraints instead of business-driven design. ### 🚀 Solution -[Architectural justification of the solution] +Isolate the Core Domain from infrastructure. The `User` entity must be a pure TypeScript class devoid of external imports or decorators. The mapping between the database rows and the Domain Entity must be handled strictly by the Secondary Adapter (e.g., via a Mapper or DTO class), ensuring the Core remains clean, testable, and completely vendor-agnostic.