mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
feat: add aggregate boundary validation (v0.7.0)
Implement DDD aggregate boundary validation to detect and prevent direct entity references across aggregate boundaries. Features: - Detect direct entity imports between aggregates - Allow only ID or Value Object references - Support multiple folder structures (domain/aggregates/*, domain/*, domain/entities/*) - Filter allowed imports (value-objects, events, repositories, services) - Critical severity level for violations - 41 comprehensive tests with 92.55% coverage - CLI output with detailed suggestions - Examples of good and bad patterns Breaking changes: None Backwards compatible: Yes
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* ❌ BAD EXAMPLE: Direct Entity Reference Across Aggregates
|
||||
*
|
||||
* Violation: Order aggregate directly imports and uses User entity from User aggregate
|
||||
*
|
||||
* Problems:
|
||||
* 1. Creates tight coupling between aggregates
|
||||
* 2. Changes to User entity affect Order aggregate
|
||||
* 3. Violates aggregate boundary principles in DDD
|
||||
* 4. Makes aggregates not independently modifiable
|
||||
*/
|
||||
|
||||
import { User } from "../user/User"
|
||||
import { Product } from "../product/Product"
|
||||
|
||||
export class Order {
|
||||
private id: string
|
||||
private user: User
|
||||
private product: Product
|
||||
private quantity: number
|
||||
|
||||
constructor(id: string, user: User, product: Product, quantity: number) {
|
||||
this.id = id
|
||||
this.user = user
|
||||
this.product = product
|
||||
this.quantity = quantity
|
||||
}
|
||||
|
||||
getUserEmail(): string {
|
||||
return this.user.email
|
||||
}
|
||||
|
||||
getProductPrice(): number {
|
||||
return this.product.price
|
||||
}
|
||||
|
||||
calculateTotal(): number {
|
||||
return this.product.price * this.quantity
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* ✅ GOOD EXAMPLE: Reference by ID
|
||||
*
|
||||
* Best Practice: Order aggregate references other aggregates only by their IDs
|
||||
*
|
||||
* Benefits:
|
||||
* 1. Loose coupling between aggregates
|
||||
* 2. Each aggregate can be modified independently
|
||||
* 3. Follows DDD aggregate boundary principles
|
||||
* 4. Clear separation of concerns
|
||||
*/
|
||||
|
||||
import { UserId } from "../user/value-objects/UserId"
|
||||
import { ProductId } from "../product/value-objects/ProductId"
|
||||
|
||||
export class Order {
|
||||
private id: string
|
||||
private userId: UserId
|
||||
private productId: ProductId
|
||||
private quantity: number
|
||||
|
||||
constructor(id: string, userId: UserId, productId: ProductId, quantity: number) {
|
||||
this.id = id
|
||||
this.userId = userId
|
||||
this.productId = productId
|
||||
this.quantity = quantity
|
||||
}
|
||||
|
||||
getUserId(): UserId {
|
||||
return this.userId
|
||||
}
|
||||
|
||||
getProductId(): ProductId {
|
||||
return this.productId
|
||||
}
|
||||
|
||||
getQuantity(): number {
|
||||
return this.quantity
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* ✅ GOOD EXAMPLE: Using Value Objects for Needed Data
|
||||
*
|
||||
* Best Practice: When Order needs specific data from other aggregates,
|
||||
* use Value Objects to store that data (denormalization)
|
||||
*
|
||||
* Benefits:
|
||||
* 1. Order aggregate has all data it needs
|
||||
* 2. No runtime dependency on other aggregates
|
||||
* 3. Better performance (no joins needed)
|
||||
* 4. Clear contract through Value Objects
|
||||
*/
|
||||
|
||||
import { UserId } from "../user/value-objects/UserId"
|
||||
import { ProductId } from "../product/value-objects/ProductId"
|
||||
|
||||
export class CustomerInfo {
|
||||
constructor(
|
||||
readonly customerId: UserId,
|
||||
readonly customerName: string,
|
||||
readonly customerEmail: string,
|
||||
) {}
|
||||
}
|
||||
|
||||
export class ProductInfo {
|
||||
constructor(
|
||||
readonly productId: ProductId,
|
||||
readonly productName: string,
|
||||
readonly productPrice: number,
|
||||
) {}
|
||||
}
|
||||
|
||||
export class Order {
|
||||
private id: string
|
||||
private customer: CustomerInfo
|
||||
private product: ProductInfo
|
||||
private quantity: number
|
||||
|
||||
constructor(id: string, customer: CustomerInfo, product: ProductInfo, quantity: number) {
|
||||
this.id = id
|
||||
this.customer = customer
|
||||
this.product = product
|
||||
this.quantity = quantity
|
||||
}
|
||||
|
||||
getCustomerEmail(): string {
|
||||
return this.customer.customerEmail
|
||||
}
|
||||
|
||||
calculateTotal(): number {
|
||||
return this.product.productPrice * this.quantity
|
||||
}
|
||||
|
||||
getCustomerInfo(): CustomerInfo {
|
||||
return this.customer
|
||||
}
|
||||
|
||||
getProductInfo(): ProductInfo {
|
||||
return this.product
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user