Knowledge base of design principles covering CQS, Hexagonal Architecture, DDD patterns, identity management, naming conventions, and OOP discipline. Used as reference by design review agents.
From dot-claudenpx claudepluginhub selrahcd/claude-marketplace --plugin dot-claudeThis skill uses the workspace's default tool permissions.
Implements Clean Architecture in Android and Kotlin Multiplatform projects: module layouts, dependency rules, UseCases, Repositories, domain models, and data layers with Room, SQLDelight, Ktor.
Delivers DB-free sandbox API regression tests for Next.js/Vitest to catch AI blind spots in self-reviewed code changes like API routes and backend logic.
Provides process, architecture, review, hiring, and testing guidelines for engineering teams relying on AI code generation.
void. They perform side effects but produce no return value.addItemAndReturnTotal(item: Item): number -- changes state AND returns data. Split into addItem(item: Item): void and getTotal(): number.save(entity: Entity): string method that persists and returns the generated ID. The caller should provide the ID upfront; save should return void.stack.pop(): Item -- mutates the stack and returns the removed element. Separate into peek(): Item (query) and remove(): void (command).@Injectable, @Entity, decorators) in domain code.TypeORM decorators: @Entity() class Order { @Column() status: string }. The domain now depends on infrastructure.fetch() or axios.get() instead of depending on a port interface like HttpClient.PostgresOrderRepository instead of receiving an OrderRepository port via constructor injection.express.Request to validate input -- the domain should receive already-validated value objects from the application layer.Aggregates
Value Objects
Entities
Domain Events
OrderShipped, PaymentReceived.OrderUpdated, DataChanged, EntityModified.Repositories
order.lineItems[0].changeQuantity(5) instead of order.changeLineItemQuantity(lineItemId, 5). The aggregate root must mediate.class Money { setAmount(n: number) { this.amount = n } }. Value objects are immutable -- create a new instance instead.order.status = "shipped" instead of order.ship() which checks preconditions.OrderUpdated -- what was updated? Use OrderShipped, OrderCancelled, or ShippingAddressChanged instead.LineItemRepository. Line items are part of the Order aggregate and should be persisted through OrderRepository.Product in the Catalog context is not the same as a Product in the Shipping context.User class used across authentication, billing, and social features. Each context should have its own model: Credentials (auth), Customer (billing), Profile (social).import { Product } from '../catalog/Product' inside the shipping context. Use an ACL to translate.string or number.class Order { id?: number } where id is optional because it is assigned by the database after insert. The order should have a known OrderId from the moment it is created.getOrder(id: string). Use a typed value object: getOrder(id: OrderId). This prevents accidentally passing a CustomerId where an OrderId is expected.AUTO_INCREMENT or SERIAL for entity identity. The domain cannot generate IDs independently of the database, coupling identity to infrastructure.number for an ID: findCustomer(id: number). Nothing prevents findCustomer(orderId) from compiling -- use distinct types.data, service, manager, handler, helper, utils, processor, info, item, stuff, object, thing.ship() not updateStatus(), approve() not setApproved(true).OrderService -- what does it do? OrderFulfillment, OrderPricing, or ShipmentScheduler each express a clear responsibility.processData(data: any) -- neither the method name nor the parameter convey meaning. Use calculateShippingCost(parcel: Parcel).UserManager -- managing what? AccountRegistration, AccessControl, or ProfileEditor describe actual behavior.handleEvent(event: Event) -- vague. Use applyDiscount(couponApplied: CouponApplied) to name the concrete action and event.utils/helpers.ts collecting unrelated functions. Each function belongs in the domain concept it serves.OrderHelper.calculateTotal(order: Order) -- the calculation belongs on Order itself: order.total().class Order { status: string; items: Item[]; total: number } with all logic in a separate OrderService. The Order class should contain ship(), cancel(), addItem(), etc.if (order.getStatus() === "paid") { order.setStatus("shipped") }. Instead: order.ship() which internally checks preconditions and transitions state.ApplicationController with 40 methods spanning user management, billing, and reporting. Split into focused classes aligned with bounded contexts.order.getItems().push(newItem) allows external code to mutate the order's item list. Provide order.addItem(newItem) which enforces invariants.