diff --git a/architectures/clean-architecture/implementation-guide.md b/architectures/clean-architecture/implementation-guide.md index 2590511..5fcefe1 100644 --- a/architectures/clean-architecture/implementation-guide.md +++ b/architectures/clean-architecture/implementation-guide.md @@ -1,34 +1,23 @@ --- -technology: Clean-architecture -domain: architecture +technology: Clean Architecture +domain: Architecture level: Senior/Architect version: Latest -tags: [architecture, best-practices] +tags: [architecture, clean-architecture, best-practices] ai_role: System Architect last_updated: 2026-03-22 -description: AI agent blueprint constraint -topic: Clean-architecture -complexity: Architect -last_evolution: 2026-03-22 -vibe_coding_ready: true --- +# 🛠️ Clean Architecture Implementation Guide + +
+ **Executable blueprints and constraints for AI-agent code generation.** +
+ --- -description: Vibe coding guidelines and architectural constraints for Clean Architecture within the Architecture domain. -tags: [clean-architecture, architecture, best-practices, architecture] -topic: Clean Architecture -complexity: Architect -last_evolution: 2026-03-29 -vibe_coding_ready: true -technology: Clean Architecture -domain: Architecture -level: Senior/Architect -version: Latest -ai_role: Senior Clean Architecture Expert -last_updated: 2026-03-29---# Clean Architecture - Implementation Guide -## Code patterns and Anti-patterns +## 💻 Code Patterns and Anti-patterns -### Entity Relationships +### 🧩 Entity Relationships ```mermaid classDiagram @@ -47,5 +36,53 @@ classDiagram ``` ### Rules -- Dependency Inversion Principle must be strictly followed. -- Entities encapsulate the most general and high-level rules. +- **Dependency Inversion Principle** must be strictly followed. Dependencies must only point inward toward the core domain. +- **Entities** encapsulate the most general and high-level business rules. They must not depend on any outer layers. + +--- +## ⚡ The Vibe Coding Instructions (Constraints) + +### ❌ Bad Practice +```typescript +import { S3Client } from 'aws-sdk'; +import { UserEntity } from '../domain/UserEntity'; + +export class UploadUserAvatarUseCase { + constructor(private readonly s3Client: S3Client) {} + + public async execute(user: UserEntity, fileBuffer: Buffer): Promise { + const uploadResult = await this.s3Client.upload({ + Bucket: 'user-avatars', + Key: `${user.id}-avatar.png`, + Body: fileBuffer + }).promise(); + + return uploadResult.Location; + } +} +``` + +### ⚠️ Problem +The Use Case (Application layer) depends directly on an external infrastructure dependency (`aws-sdk`). This violates the Dependency Rule. The Use Case cannot be tested without mocking AWS, and changing the storage provider (e.g., to Google Cloud Storage) requires modifying the core business logic. + +### ✅ Best Practice +```typescript +import { UserEntity } from '../domain/UserEntity'; +import { IFileStoragePort } from '../ports/IFileStoragePort'; + +export class UploadUserAvatarUseCase { + constructor(private readonly fileStorage: IFileStoragePort) {} + + public async execute(user: UserEntity, fileBuffer: Buffer): Promise { + const avatarUrl = await this.fileStorage.uploadFile( + `${user.id}-avatar.png`, + fileBuffer + ); + + return avatarUrl; + } +} +``` + +### 🚀 Solution +By using the **Dependency Inversion Principle**, the Application layer defines an abstract interface (`IFileStoragePort`) that dictates its needs. The Infrastructure layer implements this interface (e.g., `S3FileStorageAdapter`). The Use Case is fully decoupled from the external framework, making it easily testable and agnostic to the storage provider. \ No newline at end of file diff --git a/architectures/cqrs/implementation-guide.md b/architectures/cqrs/implementation-guide.md index 30ef7a2..dcff488 100644 --- a/architectures/cqrs/implementation-guide.md +++ b/architectures/cqrs/implementation-guide.md @@ -1,19 +1,23 @@ --- -description: Vibe coding guidelines and architectural constraints for CQRS within the Architecture domain. -tags: [cqrs, architecture, best-practices, architecture] -topic: CQRS -complexity: Architect -last_evolution: 2026-03-29 -vibe_coding_ready: true technology: CQRS domain: Architecture level: Senior/Architect version: Latest +tags: [cqrs, architecture, best-practices] ai_role: Senior CQRS Expert -last_updated: 2026-03-29---# CQRS - Implementation Guide -## Code patterns and Anti-patterns +last_updated: 2026-03-29 +--- + +# 🛠️ CQRS Implementation Guide + +
+ **Executable blueprints and constraints for AI-agent code generation.** +
-### Entity Relationships +--- +## 💻 Code Patterns and Anti-patterns + +### 🧩 Entity Relationships ```mermaid classDiagram @@ -36,3 +40,48 @@ classDiagram ### Rules - Never return business data from a Command (only ack or id). - Queries must never mutate state. + +--- +## ⚡ The Vibe Coding Instructions (Constraints) + +### ❌ Bad Practice +```typescript +import { Database } from '../infrastructure/Database'; + +export class CreateUserCommandHandler { + constructor(private readonly db: Database) {} + + async handle(command: CreateUserCommand): Promise { + // Mutating State + const user = new User(command.name, command.email); + await this.db.users.save(user); + + // BAD: Returning full business data from a command + return user; + } +} +``` + +### ⚠️ Problem +Returning the full entity or business data from a Command Handler breaks the fundamental rule of CQS/CQRS: **A method should either change state (Command) or return data (Query), but not both.** Returning data couples the mutation logic with read requirements, making it harder to optimize reads and writes independently, and breaking the single responsibility principle. + +### ✅ Best Practice +```typescript +import { Database } from '../infrastructure/Database'; + +export class CreateUserCommandHandler { + constructor(private readonly db: Database) {} + + async handle(command: CreateUserCommand): Promise { + // Mutating State + const user = new User(command.name, command.email); + await this.db.users.save(user); + + // GOOD: Returning only the ID (or an acknowledgment) + return user.id; + } +} +``` + +### 🚀 Solution +Strictly separate Commands and Queries. A Command Handler should only return a success acknowledgment or the unique identifier of the newly created resource. If the client needs the full entity data, it should subsequently issue a separate Query (e.g., `GetUserQuery`) using the returned ID. This ensures independent scaling and maintainability of the read and write models. \ No newline at end of file diff --git a/architectures/hexagonal-architecture/implementation-guide.md b/architectures/hexagonal-architecture/implementation-guide.md index d4b5a24..cde5a43 100644 --- a/architectures/hexagonal-architecture/implementation-guide.md +++ b/architectures/hexagonal-architecture/implementation-guide.md @@ -1,5 +1,4 @@ --- -description: Hexagonal Architecture Implementation Guide rules. Defining the exact code constraints for Vibe Coding and AI. technology: Hexagonal Architecture domain: Architecture level: Senior/Architect @@ -7,10 +6,7 @@ version: Agnostic tags: [best-practices, implementation-guide, hexagonal-architecture, ports-and-adapters] ai_role: Senior Software Architect last_updated: 2026-03-22 -topic: Hexagonal Architecture -complexity: Architect -last_evolution: 2026-03-29 -vibe_coding_ready: true--- +--- # 🛠️ Hexagonal Architecture Implementation Guide @@ -168,14 +164,67 @@ export class UserController { } ``` +--- ### ❌ Bad Practice -[Need to fill in example of non-optimal code] +```typescript +// BAD: Core Domain entity directly coupled to a database ORM framework +import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('users') +export class User { + @PrimaryGeneratedColumn('uuid') + public id: string; + + @Column() + public email: string; + @Column() + public status: 'ACTIVE' | 'INACTIVE'; + + public deactivate(): void { + this.status = 'INACTIVE'; + } +} +``` ### ⚠️ Problem -[Analysis of the risks] +The Core Domain entity (`User`) is polluted with database-specific decorators (`@Entity`, `@Column`) from an external library (`typeorm`). This tightly couples the business logic to a specific ORM and database technology. If you want to change the database or ORM later, you must rewrite the core business logic. Furthermore, the domain entity cannot be easily tested in isolation without setting up the ORM environment. + +### ✅ Best Practice +```typescript +// GOOD: Core Domain is purely business logic, decoupled from infrastructure +export class User { + private _id: string; + private _email: string; + private _status: 'ACTIVE' | 'INACTIVE'; + constructor(id: string, email: string) { + this._id = id; + this._email = email; + this._status = 'ACTIVE'; + } + + public deactivate(): void { + this._status = 'INACTIVE'; + } +} + +// Data Mapper handles translating to/from the external ORM +import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity('users') +export class UserOrmEntity { + @PrimaryGeneratedColumn('uuid') + public id: string; + + @Column() + public email: string; + + @Column() + public status: 'ACTIVE' | 'INACTIVE'; +} +``` ### 🚀 Solution -[Architectural justification of the solution] +Keep the Core Domain completely pure and agnostic of any external frameworks, databases, or UI. Use mapping layers (`Adapters`) to translate the pure Domain Entities to and from database-specific representations (like ORM entities or raw SQL rows). This ensures the business logic remains portable, independently testable, and strictly focused on business rules. \ No newline at end of file