Skip to content

Commit b43e159

Browse files
Merge pull request #42 from beginwebdev2002/arch/autonomous-blueprints-evolution-11983118466449645848
docs(arch): autonomous evolution of Clean Architecture and Serverless blueprints
2 parents 6dcd12c + 9e526c7 commit b43e159

2 files changed

Lines changed: 283 additions & 14 deletions

File tree

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,191 @@
11
---
2-
description: Vibe coding guidelines and architectural constraints for Clean Architecture within the Architecture domain.
32
technology: Clean Architecture
43
domain: Architecture
54
level: Senior/Architect
65
version: Agnostic
76
tags: [architecture, system-design, clean-architecture, best-practices]
87
ai_role: Senior Architect
9-
last_updated: 2026-03-22
10-
topic: Clean Architecture
11-
complexity: Architect
12-
last_evolution: 2026-03-29
13-
vibe_coding_ready: true---
14-
8+
last_updated: 2026-03-29
9+
---
1510

1611
<div align="center">
1712
# 🏛️ Clean Architecture Production-Ready Best Practices
1813
</div>
1914
---
2015

2116
Этот инженерный директив определяет **лучшие практики (best practices)** для архитектуры Clean Architecture. Данный документ спроектирован для обеспечения максимальной масштабируемости, безопасности и качества кода при разработке приложений корпоративного уровня.
17+
2218
# Context & Scope
2319
- **Primary Goal:** Предоставить строгие архитектурные правила и практические паттерны для создания масштабируемых систем.
2420
- **Description:** A concept created by Robert C. Martin (Uncle Bob). It separates a project into concentric rings. The main rule is the Dependency Rule: dependencies can only point inward.
21+
2522
## Map of Patterns
2623
- 📊 [**Data Flow:** Request and Event Lifecycle](./data-flow.md)
2724
- 📁 [**Folder Structure:** Layering logic](./folder-structure.md)
2825
- ⚖️ [**Trade-offs:** Pros, Cons, and System Constraints](./trade-offs.md)
2926
- 🛠️ [**Implementation Guide:** Code patterns and Anti-patterns](./implementation-guide.md)
27+
3028
## Core Principles
3129

3230
1. **Isolation & Testability:** Changing a single feature doesn't break the entire business process.
3331
2. **Strict Boundaries:** Enforce rigid structural barriers between business logic and infrastructure.
3432
3. **Decoupling:** Decouple how data is stored from how it is queried and displayed.
33+
34+
## Architecture Diagram
35+
36+
```mermaid
37+
graph TD
38+
Infrastructure[Infrastructure] --> InterfaceAdapters[Interface Adapters]
39+
InterfaceAdapters --> UseCases[Use Cases]
40+
UseCases --> Domain[Domain Entities]
41+
42+
%% Added Design Token Styles for Mermaid Diagrams
43+
classDef default fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,color:#000;
44+
classDef component fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#000;
45+
classDef layout fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#000;
46+
47+
class Infrastructure component;
48+
class InterfaceAdapters component;
49+
class UseCases component;
50+
class Domain component;
51+
```
52+
53+
---
54+
55+
## 1. ORM Models Bleeding into Domain
56+
57+
### ❌ Bad Practice
58+
```typescript
59+
// Domain Entity directly inherits from TypeORM BaseEntity
60+
import { BaseEntity, Column, Entity } from 'typeorm';
61+
62+
@Entity()
63+
export class User extends BaseEntity {
64+
@Column()
65+
email: string;
66+
67+
public updateEmail(newEmail: string): void {
68+
this.email = newEmail;
69+
this.save(); // Hard infrastructure coupling
70+
}
71+
}
72+
```
73+
74+
### ⚠️ Problem
75+
The Domain layer (the core of the application) is tightly coupled with a specific third-party ORM library (`TypeORM`). This violates the Dependency Rule. Changing the database technology will require rewriting core business logic.
76+
77+
### ✅ Best Practice
78+
```typescript
79+
// Pure Domain Entity completely agnostic of infrastructure
80+
export class User {
81+
constructor(private readonly id: string, private email: string) {}
82+
83+
public updateEmail(newEmail: string): void {
84+
this.email = newEmail;
85+
}
86+
}
87+
88+
// Infrastructure Layer handles the ORM mapping
89+
@Entity('users')
90+
export class UserTypeOrmEntity {
91+
@PrimaryColumn()
92+
id: string;
93+
94+
@Column()
95+
email: string;
96+
}
97+
```
98+
99+
### 🚀 Solution
100+
Isolate your Domain models from any external libraries. Use Data Mapper patterns in the Infrastructure layer to map between pure Domain entities and ORM-specific models. This ensures your core business logic is portable and testable without a database connection.
101+
102+
---
103+
104+
## 2. Direct Infrastructure Injection into Use Cases
105+
106+
### ❌ Bad Practice
107+
```typescript
108+
import { S3Client } from 'aws-sdk';
109+
110+
export class UploadAvatarUseCase {
111+
constructor(private readonly s3Client: S3Client) {}
112+
113+
public async execute(file: Buffer): Promise<string> {
114+
// Business logic depends strictly on AWS implementation
115+
const result = await this.s3Client.upload({ Bucket: 'av', Body: file }).promise();
116+
return result.Location;
117+
}
118+
}
119+
```
120+
121+
### ⚠️ Problem
122+
The Use Case (Application layer) depends directly on an external hardware/infrastructure concern (`aws-sdk`). You cannot test this Use Case without mocking AWS S3, and you cannot switch to Azure or Google Cloud without modifying the Use Case.
123+
124+
### ✅ Best Practice
125+
```typescript
126+
// Application Layer defines the abstraction (Port)
127+
export interface IFileStorageService {
128+
uploadFile(buffer: Buffer): Promise<string>;
129+
}
130+
131+
// Application Layer Use Case depends on the abstraction
132+
export class UploadAvatarUseCase {
133+
constructor(private readonly storageService: IFileStorageService) {}
134+
135+
public async execute(file: Buffer): Promise<string> {
136+
return this.storageService.uploadFile(file);
137+
}
138+
}
139+
```
140+
141+
### 🚀 Solution
142+
Apply the Dependency Inversion Principle. The Application layer should define abstract interfaces (`Ports`) that dictate what it needs from the outside world. The Infrastructure layer implements these interfaces (`Adapters`). This guarantees true architectural decouple.
143+
144+
---
145+
146+
## 3. Fat Controllers Dictating Business Flow
147+
148+
### ❌ Bad Practice
149+
```typescript
150+
class UserController {
151+
constructor(private readonly userRepository: IUserRepository) {}
152+
153+
async registerUser(req: Request, res: Response) {
154+
const { email, password } = req.body;
155+
156+
// Controller executing business validation and flow
157+
const existing = await this.userRepository.findByEmail(email);
158+
if (existing) {
159+
return res.status(400).send('Email taken');
160+
}
161+
162+
const user = new User(email, hash(password));
163+
await this.userRepository.save(user);
164+
165+
return res.status(201).send(user);
166+
}
167+
}
168+
```
169+
170+
### ⚠️ Problem
171+
The Controller (Interface Adapters layer) contains the business rules. It directly uses the Repository, bypassing the Application Use Case layer. This makes the logic difficult to reuse across different entry points (e.g., CLI, gRPC, HTTP).
172+
173+
### ✅ Best Practice
174+
```typescript
175+
class UserController {
176+
constructor(private readonly registerUserUseCase: RegisterUserUseCase) {}
177+
178+
async registerUser(req: Request, res: Response) {
179+
// Controller solely adapts HTTP into Use Case DTOs
180+
const result = await this.registerUserUseCase.execute({
181+
email: req.body.email,
182+
password: req.body.password
183+
});
184+
185+
return res.status(201).send(result);
186+
}
187+
}
188+
```
189+
190+
### 🚀 Solution
191+
Controllers should be entirely "dumb". Their only responsibility is to parse incoming requests, pass standard DTOs to the corresponding Use Case, and format the output. All branching business logic and validation must reside inside the independent Use Case interactor.

architectures/serverless/readme.md

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,146 @@
11
---
2-
description: Vibe coding guidelines and architectural constraints for Serverless within the Architecture domain.
32
technology: Serverless
43
domain: Architecture
54
level: Senior/Architect
65
version: Agnostic
76
tags: [architecture, system-design, serverless, best-practices]
87
ai_role: Senior Architect
9-
last_updated: 2026-03-22
10-
topic: Serverless
11-
complexity: Architect
12-
last_evolution: 2026-03-29
13-
vibe_coding_ready: true---
14-
8+
last_updated: 2026-03-29
9+
---
1510

1611
<div align="center">
1712
# 🏛️ Serverless Production-Ready Best Practices
1813
</div>
1914
---
2015

2116
Этот инженерный директив определяет **лучшие практики (best practices)** для архитектуры Serverless. Данный документ спроектирован для обеспечения максимальной масштабируемости, безопасности и качества кода при разработке приложений корпоративного уровня.
17+
2218
# Context & Scope
2319
- **Primary Goal:** Предоставить строгие архитектурные правила и практические паттерны для создания масштабируемых систем.
2420
- **Description:** Developers do not manage servers at all. The entire "server" consists of bite-sized pieces of business logic (functions/Lambdas) living in the cloud.
21+
2522
## Map of Patterns
2623
- 📊 [**Data Flow:** Request and Event Lifecycle](./data-flow.md)
2724
- 📁 [**Folder Structure:** Layering logic](./folder-structure.md)
2825
- ⚖️ [**Trade-offs:** Pros, Cons, and System Constraints](./trade-offs.md)
2926
- 🛠️ [**Implementation Guide:** Code patterns and Anti-patterns](./implementation-guide.md)
27+
3028
## Core Principles
3129

3230
1. **Isolation & Testability:** Changing a single feature doesn't break the entire business process.
3331
2. **Strict Boundaries:** Enforce rigid structural barriers between business logic and infrastructure.
3432
3. **Decoupling:** Decouple how data is stored from how it is queried and displayed.
33+
34+
## Architecture Diagram
35+
36+
```mermaid
37+
graph TD
38+
Trigger[Event Trigger] --> Func1[Function 1]
39+
Trigger2[HTTP Request] --> Func2[Function 2]
40+
41+
%% Added Design Token Styles for Mermaid Diagrams
42+
classDef default fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,color:#000;
43+
classDef component fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#000;
44+
classDef layout fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#000;
45+
46+
class Func1 component;
47+
class Trigger component;
48+
class Func2 component;
49+
class Trigger2 component;
50+
```
51+
52+
---
53+
54+
## 1. The Monolithic Lambda (Fat Function)
55+
56+
### ❌ Bad Practice
57+
```javascript
58+
// A single Lambda function handling multiple distinct routes/actions
59+
exports.handler = async (event) => {
60+
const { path, httpMethod, body } = event;
61+
62+
if (path === '/users' && httpMethod === 'POST') {
63+
return await createUser(JSON.parse(body));
64+
} else if (path === '/users' && httpMethod === 'GET') {
65+
return await getUsers();
66+
} else if (path === '/products' && httpMethod === 'POST') {
67+
return await createProduct(JSON.parse(body));
68+
}
69+
70+
return { statusCode: 404, body: 'Not Found' };
71+
};
72+
```
73+
74+
### ⚠️ Problem
75+
Grouping unrelated endpoints into a single function defeats the purpose of Serverless. It increases the deployment package size (slowing down cold starts), couples business domains together, complicates IAM permissions (granting excessive privileges), and makes individual function metrics/tracing impossible.
76+
77+
### ✅ Best Practice
78+
```javascript
79+
// One function strictly mapped to one specific capability
80+
// users-create.js
81+
exports.handler = async (event) => {
82+
const payload = JSON.parse(event.body);
83+
return await createUser(payload);
84+
};
85+
86+
// users-get.js
87+
exports.handler = async (event) => {
88+
return await getUsers();
89+
};
90+
```
91+
92+
### 🚀 Solution
93+
Embrace the "Single Responsibility Principle" at the infrastructure level. Decompose logic into granular, single-purpose functions. Use an API Gateway to handle routing rather than parsing paths inside the compute layer. This ensures precise IAM scoping, accurate observability, and isolated failure domains.
94+
95+
---
96+
97+
## 2. Stateful Execution Contexts
98+
99+
### ❌ Bad Practice
100+
```javascript
101+
// In-memory cache shared across invocations incorrectly
102+
let databaseConnection = null;
103+
let userCache = []; // BAD: State assumes the same instance will be hit
104+
105+
exports.handler = async (event) => {
106+
if (!databaseConnection) {
107+
databaseConnection = await createConnection();
108+
}
109+
110+
if (userCache.length === 0) {
111+
userCache = await databaseConnection.query('SELECT * FROM users');
112+
}
113+
114+
// Mutating the array mutates state for concurrent overlapping invocations
115+
userCache.push(JSON.parse(event.body));
116+
117+
return { statusCode: 200, body: JSON.stringify(userCache) };
118+
};
119+
```
120+
121+
### ⚠️ Problem
122+
Serverless environments are ephemeral. You cannot guarantee that subsequent requests will be routed to the same container instance. Relying on local variable state leads to unpredictable race conditions, phantom data bugs, and security leaks across tenant requests.
123+
124+
### ✅ Best Practice
125+
```javascript
126+
// Safely caching connections, but keeping data strictly stateless
127+
let databaseConnection = null;
128+
129+
exports.handler = async (event) => {
130+
// Good: Reusing the connection pool across warm starts
131+
if (!databaseConnection) {
132+
databaseConnection = await createConnection();
133+
}
134+
135+
// Data state is retrieved fresh or from an external distributed cache
136+
const users = await databaseConnection.query('SELECT * FROM users');
137+
const newUser = JSON.parse(event.body);
138+
139+
await databaseConnection.query('INSERT INTO users...', newUser);
140+
141+
return { statusCode: 200, body: JSON.stringify([...users, newUser]) };
142+
};
143+
```
144+
145+
### 🚀 Solution
146+
Treat every function invocation as stateless. It is acceptable (and encouraged) to cache static assets or database connection pools globally (outside the handler) to reduce cold start latency. However, all business data, session state, or mutable variables must be strictly scoped to the handler's execution context or offloaded to Redis/DynamoDB.

0 commit comments

Comments
 (0)