一键导入
architecture
// Guides the design of safely disposable code through contracts (traits/interfaces) and dependency inversion. Use when designing new modules, refactoring existing code, or making architectural decisions about component boundaries.
// Guides the design of safely disposable code through contracts (traits/interfaces) and dependency inversion. Use when designing new modules, refactoring existing code, or making architectural decisions about component boundaries.
| name | architecture |
| description | Guides the design of safely disposable code through contracts (traits/interfaces) and dependency inversion. Use when designing new modules, refactoring existing code, or making architectural decisions about component boundaries. |
Every implementation should be disposable. The system's correctness is defined by its contracts, not by any particular implementation behind them.
Define what a component does (contract), not how it does it (implementation). Any implementation that satisfies the contract is interchangeable.
A component is "safely disposable" when:
Start with the contract. Write the trait/interface before any implementation.
Rust:
pub trait UserRepository {
fn find_by_id(&self, id: UserId) -> Result<Option<User>, RepoError>;
fn save(&self, user: &User) -> Result<(), RepoError>;
}
Go:
type UserRepository interface {
FindByID(ctx context.Context, id UserID) (*User, error)
Save(ctx context.Context, user *User) error
}
TypeScript:
interface UserRepository {
findById(id: UserId): Promise<User | null>;
save(user: User): Promise<void>;
}
Rules:
UserRepository, not PostgresUserStore)Errors are part of the contract. Define domain-level error types that hide infrastructure details.
#[derive(Debug, thiserror::Error)]
pub enum RepoError {
#[error("entity not found: {0}")]
NotFound(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("internal error")]
Internal(#[source] Box<dyn std::error::Error + Send + Sync>),
}
The Internal variant wraps infrastructure errors without leaking them into the contract.
Each implementation is a disposable artifact. Write it knowing it can be thrown away.
pub struct PgUserRepository {
pool: PgPool,
}
impl UserRepository for PgUserRepository {
fn find_by_id(&self, id: UserId) -> Result<Option<User>, RepoError> {
// Postgres-specific code here.
// This entire struct is disposable.
}
fn save(&self, user: &User) -> Result<(), RepoError> {
// ...
}
}
Consumers accept the contract, never the concrete type.
pub struct UserService<R: UserRepository> {
repo: R,
}
impl<R: UserRepository> UserService<R> {
pub fn new(repo: R) -> Self {
Self { repo }
}
pub fn get_user(&self, id: UserId) -> Result<Option<User>, RepoError> {
self.repo.find_by_id(id)
}
}
In Go, this is implicit — accept the interface:
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
Write tests that verify the contract, not the implementation. These tests can be reused across implementations.
// A test suite that works for ANY UserRepository implementation.
fn test_repository_contract(repo: &impl UserRepository) {
let user = User::new("test@example.com");
repo.save(&user).unwrap();
let found = repo.find_by_id(user.id()).unwrap();
assert_eq!(found, Some(user));
}
#[test]
fn pg_repo_satisfies_contract() {
let repo = PgUserRepository::new(test_pool());
test_repository_contract(&repo);
}
#[test]
fn in_memory_repo_satisfies_contract() {
let repo = InMemoryUserRepository::new();
test_repository_contract(&repo);
}
Organize code so that dependencies always point inward toward the domain:
src/
├── domain/ # Contracts + domain types (zero external deps)
│ ├── model.rs # Domain entities and value objects
│ ├── repo.rs # Repository contracts (traits)
│ └── service.rs # Domain services using contracts
├── infra/ # Disposable implementations
│ ├── pg_repo.rs # Postgres implementation
│ └── http.rs # HTTP handlers
└── main.rs # Wiring (connects contracts to implementations)
domain/ defines contracts and types. It imports nothing from infra/.infra/ implements contracts. It imports from domain/.main.rs wires implementations to contracts.Before considering a component done, verify:
| Anti-Pattern | Problem | Fix |
|---|---|---|
| Fat contract | 10+ methods, hard to implement | Split into focused contracts |
| Leaky contract | Infrastructure types in signatures | Use domain types only |
| Concrete dependency | Consumer imports the struct directly | Accept the trait/interface |
| God module | One module does everything | Extract contracts and split |
| Premature abstraction | Contract with only one possible implementation forever | Wait until there's a reason to abstract |
Not everything needs a contract. Skip abstraction when:
Start concrete, extract a contract when the second use case appears or when you need testability.
Performs manual hands-on testing of a web application using playwright-cli. Spawns the dev server if needed, navigates to pages, performs browser actions, captures screenshots, checks outcomes, and produces a structured test report. Use when the user wants to visually verify a web feature, perform exploratory testing, or validate UI behavior.
Request a security expert assessment for code changes that touch child process spawning, file system access, configuration loading, or environment variable handling. Use when the Reviewer identifies security-sensitive changes in the MCP-LSP bridge.