@mytechtoday/augment-extensions 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -1
- package/README.md +3 -3
- package/augment-extensions/domain-rules/software-architecture/README.md +143 -0
- package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -0
- package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -0
- package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -0
- package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -0
- package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -0
- package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -0
- package/augment-extensions/domain-rules/software-architecture/module.json +119 -0
- package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -0
- package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -0
- package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -0
- package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -0
- package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -0
- package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -0
- package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -0
- package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -0
- package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -0
- package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -0
- package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -0
- package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -0
- package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -0
- package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -0
- package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -0
- package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -0
- package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -0
- package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -0
- package/augment-extensions/workflows/beads/rules/workflow.md +1 -1
- package/modules.md +40 -3
- package/package.json +1 -1
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
# Layered Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document covers layered architecture patterns including MVC, N-tier, hexagonal architecture, and pipe-and-filter patterns for organizing software systems.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Knowledge
|
|
10
|
+
|
|
11
|
+
### What is Layered Architecture?
|
|
12
|
+
|
|
13
|
+
**Definition**
|
|
14
|
+
- Organizes system into horizontal layers
|
|
15
|
+
- Each layer has specific responsibility
|
|
16
|
+
- Layers communicate through well-defined interfaces
|
|
17
|
+
- Higher layers depend on lower layers
|
|
18
|
+
- Promotes separation of concerns
|
|
19
|
+
|
|
20
|
+
**Core Principles**
|
|
21
|
+
- Separation of Concerns: Each layer has distinct responsibility
|
|
22
|
+
- Dependency Rule: Dependencies point downward
|
|
23
|
+
- Abstraction: Layers hide implementation details
|
|
24
|
+
- Testability: Layers can be tested independently
|
|
25
|
+
|
|
26
|
+
**Common Layers**
|
|
27
|
+
1. **Presentation Layer**: UI, user interaction
|
|
28
|
+
2. **Application Layer**: Use cases, orchestration
|
|
29
|
+
3. **Domain Layer**: Business logic, entities
|
|
30
|
+
4. **Infrastructure Layer**: Database, external services
|
|
31
|
+
|
|
32
|
+
### Types of Layered Architectures
|
|
33
|
+
|
|
34
|
+
**Strict Layering**
|
|
35
|
+
- Layer can only access layer directly below
|
|
36
|
+
- More rigid, better isolation
|
|
37
|
+
- Can lead to pass-through methods
|
|
38
|
+
|
|
39
|
+
**Relaxed Layering**
|
|
40
|
+
- Layer can access any layer below
|
|
41
|
+
- More flexible, less boilerplate
|
|
42
|
+
- Risk of tight coupling
|
|
43
|
+
|
|
44
|
+
**Open vs Closed Layers**
|
|
45
|
+
- Closed: Must go through layer
|
|
46
|
+
- Open: Can skip layer
|
|
47
|
+
- Mix of both in practice
|
|
48
|
+
|
|
49
|
+
### When to Use Layered Architecture
|
|
50
|
+
|
|
51
|
+
**Good Fit**
|
|
52
|
+
- Traditional enterprise applications
|
|
53
|
+
- CRUD-heavy applications
|
|
54
|
+
- Team familiar with pattern
|
|
55
|
+
- Clear separation of concerns needed
|
|
56
|
+
- Monolithic applications
|
|
57
|
+
|
|
58
|
+
**Poor Fit**
|
|
59
|
+
- Microservices (too much overhead per service)
|
|
60
|
+
- Simple applications (over-engineering)
|
|
61
|
+
- High-performance requirements (layer overhead)
|
|
62
|
+
- Rapidly changing requirements
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Skills
|
|
67
|
+
|
|
68
|
+
### Classic Three-Tier Architecture
|
|
69
|
+
|
|
70
|
+
**Presentation Tier**
|
|
71
|
+
- User interface
|
|
72
|
+
- Input validation
|
|
73
|
+
- Display logic
|
|
74
|
+
- Examples: Web pages, mobile apps, desktop UI
|
|
75
|
+
|
|
76
|
+
**Business Logic Tier**
|
|
77
|
+
- Business rules
|
|
78
|
+
- Workflow orchestration
|
|
79
|
+
- Data validation
|
|
80
|
+
- Calculations and transformations
|
|
81
|
+
|
|
82
|
+
**Data Access Tier**
|
|
83
|
+
- Database operations
|
|
84
|
+
- CRUD operations
|
|
85
|
+
- Query optimization
|
|
86
|
+
- Connection management
|
|
87
|
+
|
|
88
|
+
**Architecture Diagram**
|
|
89
|
+
```
|
|
90
|
+
┌─────────────────────────┐
|
|
91
|
+
│ Presentation Tier │ (Web/Mobile/Desktop)
|
|
92
|
+
├─────────────────────────┤
|
|
93
|
+
│ Business Logic Tier │ (Application Server)
|
|
94
|
+
├─────────────────────────┤
|
|
95
|
+
│ Data Access Tier │ (Database Server)
|
|
96
|
+
└─────────────────────────┘
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### MVC (Model-View-Controller)
|
|
100
|
+
|
|
101
|
+
**Components**
|
|
102
|
+
|
|
103
|
+
**Model**
|
|
104
|
+
- Business data and logic
|
|
105
|
+
- State management
|
|
106
|
+
- Data validation
|
|
107
|
+
- Notifies views of changes
|
|
108
|
+
|
|
109
|
+
**View**
|
|
110
|
+
- Presentation logic
|
|
111
|
+
- Displays data from model
|
|
112
|
+
- User interface
|
|
113
|
+
- No business logic
|
|
114
|
+
|
|
115
|
+
**Controller**
|
|
116
|
+
- Handles user input
|
|
117
|
+
- Updates model
|
|
118
|
+
- Selects view
|
|
119
|
+
- Orchestrates flow
|
|
120
|
+
|
|
121
|
+
**Flow**
|
|
122
|
+
```
|
|
123
|
+
User → Controller → Model → View → User
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Example Structure**
|
|
127
|
+
```
|
|
128
|
+
app/
|
|
129
|
+
├── controllers/
|
|
130
|
+
│ ├── UserController.java
|
|
131
|
+
│ └── OrderController.java
|
|
132
|
+
├── models/
|
|
133
|
+
│ ├── User.java
|
|
134
|
+
│ └── Order.java
|
|
135
|
+
└── views/
|
|
136
|
+
├── user-list.jsp
|
|
137
|
+
└── order-details.jsp
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Clean Architecture (Hexagonal/Onion)
|
|
141
|
+
|
|
142
|
+
**Layers (Inside-Out)**
|
|
143
|
+
|
|
144
|
+
**Domain Layer (Core)**
|
|
145
|
+
- Entities
|
|
146
|
+
- Value objects
|
|
147
|
+
- Domain services
|
|
148
|
+
- Business rules
|
|
149
|
+
- No external dependencies
|
|
150
|
+
|
|
151
|
+
**Application Layer**
|
|
152
|
+
- Use cases
|
|
153
|
+
- Application services
|
|
154
|
+
- Orchestration
|
|
155
|
+
- Depends only on domain
|
|
156
|
+
|
|
157
|
+
**Infrastructure Layer (Outer)**
|
|
158
|
+
- Database implementations
|
|
159
|
+
- External services
|
|
160
|
+
- Frameworks
|
|
161
|
+
- Depends on application/domain
|
|
162
|
+
|
|
163
|
+
**Presentation Layer (Outer)**
|
|
164
|
+
- Controllers
|
|
165
|
+
- API endpoints
|
|
166
|
+
- UI components
|
|
167
|
+
- Depends on application
|
|
168
|
+
|
|
169
|
+
**Dependency Rule**
|
|
170
|
+
- Dependencies point inward
|
|
171
|
+
- Inner layers know nothing of outer layers
|
|
172
|
+
- Use interfaces for inversion
|
|
173
|
+
|
|
174
|
+
**Architecture Diagram**
|
|
175
|
+
```
|
|
176
|
+
┌─────────────────────────────────────┐
|
|
177
|
+
│ Presentation & Infrastructure │
|
|
178
|
+
│ ┌───────────────────────────────┐ │
|
|
179
|
+
│ │ Application Layer │ │
|
|
180
|
+
│ │ ┌─────────────────────────┐ │ │
|
|
181
|
+
│ │ │ Domain Layer │ │ │
|
|
182
|
+
│ │ │ (Entities, Rules) │ │ │
|
|
183
|
+
│ │ └─────────────────────────┘ │ │
|
|
184
|
+
│ └───────────────────────────────┘ │
|
|
185
|
+
└─────────────────────────────────────┘
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Pipe-and-Filter Pattern
|
|
189
|
+
|
|
190
|
+
**Concept**
|
|
191
|
+
- Data flows through series of processing steps
|
|
192
|
+
- Each filter transforms data
|
|
193
|
+
- Pipes connect filters
|
|
194
|
+
- Filters are independent and reusable
|
|
195
|
+
|
|
196
|
+
**Components**
|
|
197
|
+
|
|
198
|
+
**Filter**
|
|
199
|
+
- Processes data
|
|
200
|
+
- Single responsibility
|
|
201
|
+
- Stateless (ideally)
|
|
202
|
+
- Reusable
|
|
203
|
+
|
|
204
|
+
**Pipe**
|
|
205
|
+
- Connects filters
|
|
206
|
+
- Transfers data
|
|
207
|
+
- Can buffer data
|
|
208
|
+
- Examples: streams, queues
|
|
209
|
+
|
|
210
|
+
**Use Cases**
|
|
211
|
+
- Data processing pipelines
|
|
212
|
+
- Compiler stages
|
|
213
|
+
- Image processing
|
|
214
|
+
- ETL (Extract, Transform, Load)
|
|
215
|
+
|
|
216
|
+
**Example**
|
|
217
|
+
```
|
|
218
|
+
Input → [Validate] → [Transform] → [Enrich] → [Store] → Output
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Examples
|
|
224
|
+
|
|
225
|
+
### Clean Architecture Implementation
|
|
226
|
+
|
|
227
|
+
**Domain Layer**
|
|
228
|
+
```java
|
|
229
|
+
// Entity
|
|
230
|
+
public class Order {
|
|
231
|
+
private final OrderId id;
|
|
232
|
+
private final CustomerId customerId;
|
|
233
|
+
private final List<OrderItem> items;
|
|
234
|
+
private OrderStatus status;
|
|
235
|
+
private final Money total;
|
|
236
|
+
|
|
237
|
+
public Order(OrderId id, CustomerId customerId, List<OrderItem> items) {
|
|
238
|
+
this.id = id;
|
|
239
|
+
this.customerId = customerId;
|
|
240
|
+
this.items = new ArrayList<>(items);
|
|
241
|
+
this.status = OrderStatus.PENDING;
|
|
242
|
+
this.total = calculateTotal(items);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public void confirm() {
|
|
246
|
+
if (status != OrderStatus.PENDING) {
|
|
247
|
+
throw new IllegalStateException("Order must be pending to confirm");
|
|
248
|
+
}
|
|
249
|
+
this.status = OrderStatus.CONFIRMED;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private Money calculateTotal(List<OrderItem> items) {
|
|
253
|
+
return items.stream()
|
|
254
|
+
.map(OrderItem::getSubtotal)
|
|
255
|
+
.reduce(Money.ZERO, Money::add);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Value Object
|
|
260
|
+
public class Money {
|
|
261
|
+
private final BigDecimal amount;
|
|
262
|
+
private final Currency currency;
|
|
263
|
+
|
|
264
|
+
public Money(BigDecimal amount, Currency currency) {
|
|
265
|
+
if (amount.compareTo(BigDecimal.ZERO) < 0) {
|
|
266
|
+
throw new IllegalArgumentException("Amount cannot be negative");
|
|
267
|
+
}
|
|
268
|
+
this.amount = amount;
|
|
269
|
+
this.currency = currency;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
public Money add(Money other) {
|
|
273
|
+
if (!this.currency.equals(other.currency)) {
|
|
274
|
+
throw new IllegalArgumentException("Cannot add different currencies");
|
|
275
|
+
}
|
|
276
|
+
return new Money(this.amount.add(other.amount), this.currency);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Repository Interface (in domain)
|
|
281
|
+
public interface OrderRepository {
|
|
282
|
+
Order findById(OrderId id);
|
|
283
|
+
void save(Order order);
|
|
284
|
+
List<Order> findByCustomerId(CustomerId customerId);
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Application Layer**
|
|
289
|
+
```java
|
|
290
|
+
// Use Case
|
|
291
|
+
public class PlaceOrderUseCase {
|
|
292
|
+
private final OrderRepository orderRepository;
|
|
293
|
+
private final ProductRepository productRepository;
|
|
294
|
+
private final PaymentGateway paymentGateway;
|
|
295
|
+
|
|
296
|
+
public PlaceOrderUseCase(
|
|
297
|
+
OrderRepository orderRepository,
|
|
298
|
+
ProductRepository productRepository,
|
|
299
|
+
PaymentGateway paymentGateway
|
|
300
|
+
) {
|
|
301
|
+
this.orderRepository = orderRepository;
|
|
302
|
+
this.productRepository = productRepository;
|
|
303
|
+
this.paymentGateway = paymentGateway;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
public OrderId execute(PlaceOrderCommand command) {
|
|
307
|
+
// Validate products exist
|
|
308
|
+
List<Product> products = productRepository.findByIds(
|
|
309
|
+
command.getProductIds()
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
if (products.size() != command.getProductIds().size()) {
|
|
313
|
+
throw new ProductNotFoundException();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Create order
|
|
317
|
+
List<OrderItem> items = createOrderItems(products, command);
|
|
318
|
+
Order order = new Order(
|
|
319
|
+
OrderId.generate(),
|
|
320
|
+
command.getCustomerId(),
|
|
321
|
+
items
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Process payment
|
|
325
|
+
PaymentResult result = paymentGateway.charge(
|
|
326
|
+
command.getPaymentMethod(),
|
|
327
|
+
order.getTotal()
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (result.isSuccessful()) {
|
|
331
|
+
order.confirm();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Save order
|
|
335
|
+
orderRepository.save(order);
|
|
336
|
+
|
|
337
|
+
return order.getId();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Command (Input DTO)
|
|
342
|
+
public class PlaceOrderCommand {
|
|
343
|
+
private final CustomerId customerId;
|
|
344
|
+
private final List<ProductId> productIds;
|
|
345
|
+
private final Map<ProductId, Integer> quantities;
|
|
346
|
+
private final PaymentMethod paymentMethod;
|
|
347
|
+
|
|
348
|
+
// Constructor, getters...
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Infrastructure Layer**
|
|
353
|
+
```java
|
|
354
|
+
// Repository Implementation
|
|
355
|
+
@Repository
|
|
356
|
+
public class JpaOrderRepository implements OrderRepository {
|
|
357
|
+
private final EntityManager entityManager;
|
|
358
|
+
|
|
359
|
+
@Override
|
|
360
|
+
public Order findById(OrderId id) {
|
|
361
|
+
OrderEntity entity = entityManager.find(
|
|
362
|
+
OrderEntity.class,
|
|
363
|
+
id.getValue()
|
|
364
|
+
);
|
|
365
|
+
return entity != null ? entity.toDomain() : null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
@Override
|
|
369
|
+
public void save(Order order) {
|
|
370
|
+
OrderEntity entity = OrderEntity.fromDomain(order);
|
|
371
|
+
entityManager.merge(entity);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// JPA Entity (Infrastructure concern)
|
|
376
|
+
@Entity
|
|
377
|
+
@Table(name = "orders")
|
|
378
|
+
class OrderEntity {
|
|
379
|
+
@Id
|
|
380
|
+
private String id;
|
|
381
|
+
|
|
382
|
+
@Column(name = "customer_id")
|
|
383
|
+
private String customerId;
|
|
384
|
+
|
|
385
|
+
@OneToMany(cascade = CascadeType.ALL)
|
|
386
|
+
private List<OrderItemEntity> items;
|
|
387
|
+
|
|
388
|
+
@Enumerated(EnumType.STRING)
|
|
389
|
+
private OrderStatus status;
|
|
390
|
+
|
|
391
|
+
// Mapping methods
|
|
392
|
+
public Order toDomain() {
|
|
393
|
+
return new Order(
|
|
394
|
+
new OrderId(this.id),
|
|
395
|
+
new CustomerId(this.customerId),
|
|
396
|
+
this.items.stream()
|
|
397
|
+
.map(OrderItemEntity::toDomain)
|
|
398
|
+
.collect(Collectors.toList())
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
public static OrderEntity fromDomain(Order order) {
|
|
403
|
+
OrderEntity entity = new OrderEntity();
|
|
404
|
+
entity.id = order.getId().getValue();
|
|
405
|
+
entity.customerId = order.getCustomerId().getValue();
|
|
406
|
+
entity.items = order.getItems().stream()
|
|
407
|
+
.map(OrderItemEntity::fromDomain)
|
|
408
|
+
.collect(Collectors.toList());
|
|
409
|
+
entity.status = order.getStatus();
|
|
410
|
+
return entity;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Presentation Layer**
|
|
416
|
+
```java
|
|
417
|
+
// REST Controller
|
|
418
|
+
@RestController
|
|
419
|
+
@RequestMapping("/api/orders")
|
|
420
|
+
public class OrderController {
|
|
421
|
+
private final PlaceOrderUseCase placeOrderUseCase;
|
|
422
|
+
private final GetOrderUseCase getOrderUseCase;
|
|
423
|
+
|
|
424
|
+
@PostMapping
|
|
425
|
+
public ResponseEntity<OrderResponse> placeOrder(
|
|
426
|
+
@RequestBody PlaceOrderRequest request
|
|
427
|
+
) {
|
|
428
|
+
// Map request to command
|
|
429
|
+
PlaceOrderCommand command = new PlaceOrderCommand(
|
|
430
|
+
new CustomerId(request.getCustomerId()),
|
|
431
|
+
request.getProductIds().stream()
|
|
432
|
+
.map(ProductId::new)
|
|
433
|
+
.collect(Collectors.toList()),
|
|
434
|
+
request.getQuantities(),
|
|
435
|
+
request.getPaymentMethod()
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Execute use case
|
|
439
|
+
OrderId orderId = placeOrderUseCase.execute(command);
|
|
440
|
+
|
|
441
|
+
// Map to response
|
|
442
|
+
return ResponseEntity.ok(new OrderResponse(orderId.getValue()));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
@GetMapping("/{orderId}")
|
|
446
|
+
public ResponseEntity<OrderDetailsResponse> getOrder(
|
|
447
|
+
@PathVariable String orderId
|
|
448
|
+
) {
|
|
449
|
+
Order order = getOrderUseCase.execute(new OrderId(orderId));
|
|
450
|
+
return ResponseEntity.ok(OrderDetailsResponse.from(order));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Request DTO
|
|
455
|
+
public class PlaceOrderRequest {
|
|
456
|
+
private String customerId;
|
|
457
|
+
private List<String> productIds;
|
|
458
|
+
private Map<String, Integer> quantities;
|
|
459
|
+
private PaymentMethod paymentMethod;
|
|
460
|
+
|
|
461
|
+
// Getters, setters...
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### MVC Web Application
|
|
466
|
+
|
|
467
|
+
**Model**
|
|
468
|
+
```java
|
|
469
|
+
@Entity
|
|
470
|
+
public class User {
|
|
471
|
+
@Id
|
|
472
|
+
@GeneratedValue
|
|
473
|
+
private Long id;
|
|
474
|
+
|
|
475
|
+
private String username;
|
|
476
|
+
private String email;
|
|
477
|
+
private String passwordHash;
|
|
478
|
+
|
|
479
|
+
// Business logic
|
|
480
|
+
public boolean authenticate(String password) {
|
|
481
|
+
return BCrypt.checkpw(password, this.passwordHash);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
public void updateEmail(String newEmail) {
|
|
485
|
+
if (!isValidEmail(newEmail)) {
|
|
486
|
+
throw new IllegalArgumentException("Invalid email");
|
|
487
|
+
}
|
|
488
|
+
this.email = newEmail;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Controller**
|
|
494
|
+
```java
|
|
495
|
+
@Controller
|
|
496
|
+
@RequestMapping("/users")
|
|
497
|
+
public class UserController {
|
|
498
|
+
private final UserService userService;
|
|
499
|
+
|
|
500
|
+
@GetMapping
|
|
501
|
+
public String listUsers(Model model) {
|
|
502
|
+
List<User> users = userService.findAll();
|
|
503
|
+
model.addAttribute("users", users);
|
|
504
|
+
return "user-list";
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
@GetMapping("/{id}")
|
|
508
|
+
public String viewUser(@PathVariable Long id, Model model) {
|
|
509
|
+
User user = userService.findById(id);
|
|
510
|
+
model.addAttribute("user", user);
|
|
511
|
+
return "user-details";
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
@PostMapping
|
|
515
|
+
public String createUser(@ModelAttribute UserForm form,
|
|
516
|
+
RedirectAttributes redirectAttributes) {
|
|
517
|
+
User user = userService.create(
|
|
518
|
+
form.getUsername(),
|
|
519
|
+
form.getEmail(),
|
|
520
|
+
form.getPassword()
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
redirectAttributes.addFlashAttribute(
|
|
524
|
+
"message",
|
|
525
|
+
"User created successfully"
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
return "redirect:/users/" + user.getId();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**View (Thymeleaf)**
|
|
534
|
+
```html
|
|
535
|
+
<!DOCTYPE html>
|
|
536
|
+
<html xmlns:th="http://www.thymeleaf.org">
|
|
537
|
+
<head>
|
|
538
|
+
<title>User List</title>
|
|
539
|
+
</head>
|
|
540
|
+
<body>
|
|
541
|
+
<h1>Users</h1>
|
|
542
|
+
|
|
543
|
+
<table>
|
|
544
|
+
<thead>
|
|
545
|
+
<tr>
|
|
546
|
+
<th>ID</th>
|
|
547
|
+
<th>Username</th>
|
|
548
|
+
<th>Email</th>
|
|
549
|
+
<th>Actions</th>
|
|
550
|
+
</tr>
|
|
551
|
+
</thead>
|
|
552
|
+
<tbody>
|
|
553
|
+
<tr th:each="user : ${users}">
|
|
554
|
+
<td th:text="${user.id}"></td>
|
|
555
|
+
<td th:text="${user.username}"></td>
|
|
556
|
+
<td th:text="${user.email}"></td>
|
|
557
|
+
<td>
|
|
558
|
+
<a th:href="@{/users/{id}(id=${user.id})}">View</a>
|
|
559
|
+
</td>
|
|
560
|
+
</tr>
|
|
561
|
+
</tbody>
|
|
562
|
+
</table>
|
|
563
|
+
</body>
|
|
564
|
+
</html>
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Pipe-and-Filter Data Processing
|
|
568
|
+
|
|
569
|
+
**Filter Interface**
|
|
570
|
+
```java
|
|
571
|
+
public interface Filter<I, O> {
|
|
572
|
+
O process(I input);
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**Concrete Filters**
|
|
577
|
+
```java
|
|
578
|
+
// Validation Filter
|
|
579
|
+
public class ValidationFilter implements Filter<RawData, ValidatedData> {
|
|
580
|
+
@Override
|
|
581
|
+
public ValidatedData process(RawData input) {
|
|
582
|
+
if (input.getId() == null || input.getId().isEmpty()) {
|
|
583
|
+
throw new ValidationException("ID is required");
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (input.getValue() < 0) {
|
|
587
|
+
throw new ValidationException("Value must be positive");
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return new ValidatedData(input.getId(), input.getValue());
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Transformation Filter
|
|
595
|
+
public class TransformationFilter implements Filter<ValidatedData, TransformedData> {
|
|
596
|
+
@Override
|
|
597
|
+
public TransformedData process(ValidatedData input) {
|
|
598
|
+
// Apply business transformation
|
|
599
|
+
double transformedValue = input.getValue() * 1.1; // 10% markup
|
|
600
|
+
|
|
601
|
+
return new TransformedData(
|
|
602
|
+
input.getId(),
|
|
603
|
+
transformedValue,
|
|
604
|
+
LocalDateTime.now()
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Enrichment Filter
|
|
610
|
+
public class EnrichmentFilter implements Filter<TransformedData, EnrichedData> {
|
|
611
|
+
private final ExternalService externalService;
|
|
612
|
+
|
|
613
|
+
@Override
|
|
614
|
+
public EnrichedData process(TransformedData input) {
|
|
615
|
+
// Fetch additional data
|
|
616
|
+
AdditionalInfo info = externalService.getInfo(input.getId());
|
|
617
|
+
|
|
618
|
+
return new EnrichedData(
|
|
619
|
+
input.getId(),
|
|
620
|
+
input.getValue(),
|
|
621
|
+
input.getTimestamp(),
|
|
622
|
+
info
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
**Pipeline**
|
|
629
|
+
```java
|
|
630
|
+
public class Pipeline<I, O> {
|
|
631
|
+
private final List<Filter<?, ?>> filters = new ArrayList<>();
|
|
632
|
+
|
|
633
|
+
public <T> Pipeline<I, T> addFilter(Filter<O, T> filter) {
|
|
634
|
+
filters.add(filter);
|
|
635
|
+
return (Pipeline<I, T>) this;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
public O execute(I input) {
|
|
639
|
+
Object current = input;
|
|
640
|
+
|
|
641
|
+
for (Filter filter : filters) {
|
|
642
|
+
current = ((Filter<Object, Object>) filter).process(current);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return (O) current;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Usage
|
|
650
|
+
Pipeline<RawData, EnrichedData> pipeline = new Pipeline<>()
|
|
651
|
+
.addFilter(new ValidationFilter())
|
|
652
|
+
.addFilter(new TransformationFilter())
|
|
653
|
+
.addFilter(new EnrichmentFilter());
|
|
654
|
+
|
|
655
|
+
EnrichedData result = pipeline.execute(rawData);
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## Understanding
|
|
661
|
+
|
|
662
|
+
### Advantages of Layered Architecture
|
|
663
|
+
|
|
664
|
+
**Separation of Concerns**
|
|
665
|
+
- Clear responsibilities
|
|
666
|
+
- Easier to understand
|
|
667
|
+
- Focused modules
|
|
668
|
+
- Reduced complexity
|
|
669
|
+
|
|
670
|
+
**Testability**
|
|
671
|
+
- Test layers independently
|
|
672
|
+
- Mock dependencies
|
|
673
|
+
- Unit test business logic
|
|
674
|
+
- Integration test layers
|
|
675
|
+
|
|
676
|
+
**Maintainability**
|
|
677
|
+
- Changes isolated to layers
|
|
678
|
+
- Clear structure
|
|
679
|
+
- Easy to locate code
|
|
680
|
+
- Reduced coupling
|
|
681
|
+
|
|
682
|
+
**Reusability**
|
|
683
|
+
- Reuse business logic
|
|
684
|
+
- Share data access code
|
|
685
|
+
- Common infrastructure
|
|
686
|
+
- Consistent patterns
|
|
687
|
+
|
|
688
|
+
**Team Organization**
|
|
689
|
+
- Teams can own layers
|
|
690
|
+
- Parallel development
|
|
691
|
+
- Clear interfaces
|
|
692
|
+
- Reduced conflicts
|
|
693
|
+
|
|
694
|
+
### Challenges and Disadvantages
|
|
695
|
+
|
|
696
|
+
**Performance Overhead**
|
|
697
|
+
- Layer traversal cost
|
|
698
|
+
- Data transformation between layers
|
|
699
|
+
- Potential for chattiness
|
|
700
|
+
- Memory overhead
|
|
701
|
+
|
|
702
|
+
**Rigidity**
|
|
703
|
+
- Can be over-engineered
|
|
704
|
+
- Boilerplate code
|
|
705
|
+
- Pass-through methods
|
|
706
|
+
- Difficult to change layer structure
|
|
707
|
+
|
|
708
|
+
**Anemic Domain Model**
|
|
709
|
+
- Business logic in service layer
|
|
710
|
+
- Entities become data holders
|
|
711
|
+
- Violates OOP principles
|
|
712
|
+
- Reduced encapsulation
|
|
713
|
+
|
|
714
|
+
**Tight Coupling Risk**
|
|
715
|
+
- Layers can become coupled
|
|
716
|
+
- Shared data structures
|
|
717
|
+
- Leaky abstractions
|
|
718
|
+
- Difficult to change
|
|
719
|
+
|
|
720
|
+
### Best Practices
|
|
721
|
+
|
|
722
|
+
**Dependency Management**
|
|
723
|
+
- Dependencies point downward
|
|
724
|
+
- Use interfaces for abstraction
|
|
725
|
+
- Dependency injection
|
|
726
|
+
- Avoid circular dependencies
|
|
727
|
+
|
|
728
|
+
**Layer Responsibilities**
|
|
729
|
+
- Keep layers focused
|
|
730
|
+
- Don't skip layers (strict layering)
|
|
731
|
+
- Minimize pass-through methods
|
|
732
|
+
- Use DTOs between layers
|
|
733
|
+
|
|
734
|
+
**Domain-Driven Design**
|
|
735
|
+
- Rich domain models
|
|
736
|
+
- Business logic in domain layer
|
|
737
|
+
- Entities with behavior
|
|
738
|
+
- Value objects for immutability
|
|
739
|
+
|
|
740
|
+
**Testing Strategy**
|
|
741
|
+
- Unit test domain layer
|
|
742
|
+
- Integration test application layer
|
|
743
|
+
- End-to-end test presentation layer
|
|
744
|
+
- Mock external dependencies
|
|
745
|
+
|
|
746
|
+
**Avoid Common Pitfalls**
|
|
747
|
+
- Don't put business logic in controllers
|
|
748
|
+
- Don't access database from presentation
|
|
749
|
+
- Don't expose domain entities to UI
|
|
750
|
+
- Don't create god services
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## References
|
|
755
|
+
|
|
756
|
+
- **Books**
|
|
757
|
+
- "Clean Architecture" by Robert C. Martin
|
|
758
|
+
- "Domain-Driven Design" by Eric Evans
|
|
759
|
+
- "Patterns of Enterprise Application Architecture" by Martin Fowler
|
|
760
|
+
- "Implementing Domain-Driven Design" by Vaughn Vernon
|
|
761
|
+
|
|
762
|
+
- **Patterns**
|
|
763
|
+
- MVC (Model-View-Controller)
|
|
764
|
+
- MVP (Model-View-Presenter)
|
|
765
|
+
- MVVM (Model-View-ViewModel)
|
|
766
|
+
- Hexagonal Architecture (Ports and Adapters)
|
|
767
|
+
- Onion Architecture
|
|
768
|
+
- Clean Architecture
|
|
769
|
+
|
|
770
|
+
- **Principles**
|
|
771
|
+
- Separation of Concerns
|
|
772
|
+
- Dependency Inversion Principle
|
|
773
|
+
- Single Responsibility Principle
|
|
774
|
+
- Interface Segregation Principle
|
|
775
|
+
|
|
776
|
+
|