--- id: wiki-2026-0508-impedance-matching title: Impedance Matching category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Object-Relational Impedance Mismatch, API Impedance] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [orm, api, architecture, integration] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: prisma --- # Impedance Matching ## 매 한 줄 > **"매 두 system 의 model / protocol 차이를 매 어디선가 흡수해야 한다 — 매 그 자리를 well-chosen 한 곳으로"**. 매 EE 의 source-load impedance match 의 metaphor — software 에서 매 OO ↔ relational, REST ↔ event, sync ↔ async, monolith ↔ microservice 사이의 mismatch 를 anti-corruption layer / DTO / ORM / adapter 로 매 흡수. ## 매 핵심 ### 매 classic mismatches - **OO ↔ Relational**: object identity vs row, inheritance vs table, association vs FK. - **REST ↔ Event-driven**: request/response vs publish/subscribe, sync vs async. - **Internal model ↔ External API**: domain entity vs DTO/contract. - **Bounded context 간**: 매 같은 단어 ("Order") 가 매 다른 의미. ### 매 흡수 위치 - **ORM** (Prisma, Drizzle, SQLAlchemy, Hibernate): OR mismatch. - **DTO / Schema** (Zod, Pydantic, Protobuf): API boundary. - **Anti-Corruption Layer (ACL)**: bounded context 간 (Evans DDD). - **Adapter / Port** (Hex / Clean architecture): 매 infra ↔ domain. - **Event envelope / outbox**: sync ↔ async. ### 매 응용 1. ORM choice / hand-rolled SQL trade-off. 2. GraphQL / tRPC / gRPC 의 schema-first contract. 3. CQRS — read model 과 write model 의 분리. 4. Strangler fig — legacy ↔ new system migration. ## 💻 패턴 ### Prisma — OR mismatch 흡수 ```ts // schema.prisma // model User { id Int @id @default(autoincrement()) email String @unique posts Post[] } import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); const user = await prisma.user.findUnique({ where: { email }, include: { posts: true } }); // 매 row → object graph (lazy/eager) automatic ``` ### DTO + Zod (API boundary) ```ts import { z } from "zod"; export const CreateUserDTO = z.object({ email: z.string().email(), name: z.string().min(1).max(100), }); export type CreateUserDTO = z.infer; // route app.post("/users", async (req, res) => { const dto = CreateUserDTO.parse(req.body); // 매 invalid 면 throw const user = await userService.create(dto); res.json(toUserResponse(user)); // 매 entity → response DTO }); ``` ### Anti-Corruption Layer ```ts // 매 legacy CRM 의 dirty model → 매 깨끗한 domain 으로 class CrmAcl { toCustomer(raw: LegacyCrmRow): Customer { return { id: CustomerId.of(raw.cust_id_v2 ?? raw.cust_id), email: Email.of(raw.email_addr.trim().toLowerCase()), tier: raw.tier_code === "P" ? "premium" : "standard", }; } } ``` ### Outbox (sync ↔ async) ```ts await prisma.$transaction([ prisma.order.create({ data: order }), prisma.outbox.create({ data: { topic: "order.created", payload: JSON.stringify(order) } }), ]); // 매 별도 worker 가 outbox → Kafka publish (at-least-once) ``` ### Hexagonal port ```ts // 매 domain side 의 port (interface) export interface PaymentGateway { charge(amount: Money, token: string): Promise; } // 매 infra side 의 adapter export class StripeAdapter implements PaymentGateway { async charge(amount: Money, token: string): Promise { const r = await stripe.paymentIntents.create({ amount: amount.cents, currency: amount.ccy, payment_method: token }); return ChargeId.of(r.id); } } ``` ### gRPC / Protobuf contract ```proto syntax = "proto3"; message User { int64 id = 1; string email = 2; string name = 3; } service UserService { rpc GetUser(GetUserRequest) returns (User); } ``` ### CQRS — read model ```ts // 매 write: domain entity → event // 매 read: denormalized view (materialized) → query const orders = await db.orderListView.where({ userId }).orderBy("createdAt", "desc"); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Simple CRUD + RDB | ORM (Prisma / Drizzle) | | Complex query / perf | hand-rolled SQL + thin mapper | | External legacy | ACL (변환 layer 명시) | | Polyglot service mesh | Protobuf + gRPC | | Sync write + async fanout | Outbox + event bus | | Read 많이, write 적게 | CQRS 의 separate read model | **기본값**: TS + Postgres 조합은 Prisma/Drizzle + Zod DTO + outbox. ## 🔗 Graph - 부모: [[Software Architecture]] · [[Integration]] - 변형: [[Anti-Corruption Layer]] · [[Hexagonal Architecture]] · [[CQRS]] - 응용: [[gRPC]] · [[Event-Driven Architecture]] - Adjacent: [[Bounded Context]] · [[DDD]] · [[Strangler Fig]] ## 🤖 LLM 활용 **언제**: legacy 통합 설계, API contract 결정, ORM vs raw SQL trade-off reasoning. **언제 X**: 매 단일 monolith + 단일 DB 의 trivial app — 매 over-engineering. ## ❌ 안티패턴 - **Leaky ORM**: 매 ORM entity 를 그대로 API 응답 — 매 schema lock-in. - **N+1 query**: 매 ORM 의 lazy load loop — include / dataloader. - **Anemic ACL**: 매 변환만 하고 의미 보존 X — 매 그냥 DTO 와 다를 바 없음. - **Distributed monolith**: 매 microservice 인데 DB schema 공유 — 매 mismatch 흡수 실패. - **At-most-once event publish**: 매 outbox 없이 commit 후 publish → 매 lost message. ## 🧪 검증 / 중복 - Verified (Fowler *PoEAA* 2002, Evans *DDD* 2003, Vernon *IDDD* 2013, Prisma docs, microservices.io). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — OR mismatch + ACL + outbox + CQRS 정리 |