"매 단일 deployable unit 으로 묶인 application". 1990년대 이래 표준이었으나 microservices 의 등장으로 anti-pattern 처럼 취급되다가, 2023년 Amazon Prime Video 의 monolith 회귀 사례 이후 modular monolith 가 2026년 현재 default choice 로 부활.
매 핵심
매 정의
매 single codebase, single build, single deployment.
매 모든 business capability 가 하나의 process 내 in-memory 호출.
매 single database 공유 (보통 RDBMS).
매 variant
Big ball of mud: 매 module boundary X. 매 anti-pattern.
Layered monolith: 매 controller / service / repo layer 분리. 매 일반적.
Modular monolith: 매 internal module boundary 명확. 매 microservices 직전 단계.
Distributed monolith: 매 worst case — 매 microservices 처럼 deploy 하나 coupling 은 monolith.
매 응용
Early-stage startup MVP — 매 fast iteration.
Internal tools — 매 traffic low, complexity low.
Modular monolith — 매 mid-size product (Shopify, Basecamp, GitHub).
💻 패턴
Layered structure (Spring Boot)
// src/main/java/com/example/app/// ├── controller/ ── HTTP boundary// ├── service/ ── business logic// ├── repository/ ── data access// └── domain/ ── entities@RestController@RequestMapping("/orders")classOrderController{privatefinalOrderServiceservice;OrderController(OrderServices){this.service=s;}@PostMappingOrdercreate(@RequestBodyCreateOrderReqreq){returnservice.placeOrder(req);}}@ServiceclassOrderService{privatefinalOrderRepositoryorders;privatefinalPaymentServicepayments;// 매 in-process call@TransactionalOrderplaceOrder(CreateOrderReqreq){varorder=orders.save(newOrder(req));payments.charge(order);// 매 same DB transactionreturnorder;}}
Modular monolith (Rails-like, internal API)
# app/modules/billing/# ├── public/ # 매 외부 module 가 import 가능# │ └── billing_api.rb# └── internal/ # 매 module 내부에서만# ├── invoice.rb# └── stripe_client.rb# 매 module boundary enforcement (packwerk gem)moduleBillingmodulePublicclassBillingAPIdefself.charge(user_id:,amount:)Internal::StripeClient.new.charge(user_id,amount)endendendend# 매 Catalog module 가 Billing 호출 — 매 public API 만classCheckoutFlowdefcall(cart)Billing::Public::BillingAPI.charge(user_id:cart.user_id,amount:cart.total)endend
Single binary deploy (Go)
// main.go — 매 entire app one binaryfuncmain(){db:=mustOpenDB()deferdb.Close()mux:=http.NewServeMux()mux.Handle("/users/",users.NewHandler(db))mux.Handle("/orders/",orders.NewHandler(db))mux.Handle("/billing/",billing.NewHandler(db))log.Fatal(http.ListenAndServe(":8080",mux))}// 매 deploy = scp binary + restart systemd// 매 zero network hop, zero serialization
Shared transaction (atomicity)
# 매 monolith 의 핵심 이점 — 매 single ACID transaction@transaction.atomicdeftransfer_funds(from_id,to_id,amount):src=Account.objects.select_for_update().get(id=from_id)dst=Account.objects.select_for_update().get(id=to_id)src.balance-=amountdst.balance+=amountsrc.save();dst.save()AuditLog.objects.create(action="transfer",amount=amount)# 매 all-or-nothing — 매 saga / 2PC 매 X
Module 경계 violation 탐지 (TypeScript)
// .dependency-cruiser.cjs
module.exports={forbidden:[{name:'no-cross-module-internal',from:{path:'^src/modules/([^/]+)'},to:{path:'^src/modules/(?!\\1)[^/]+/internal'},},],};// 매 CI 에서 검증 — 매 module boundary 매 enforce
매 결정 기준
상황
Approach
팀 < 20명, product 단일
Modular monolith
MVP / startup early stage
Layered monolith
Independent scaling 필요 (e.g. ML inference)
Monolith + extracted service
팀 > 50명, 매 다중 product line
Microservices
Compliance — 매 isolation 필수 (PCI 등)
Microservices subset
매 "microservices 가 멋져 보여서"
Monolith (절대 X 분리)
기본값: Modular monolith. 매 boundary 가 stable 해진 후 strangler-fig pattern 으로 extract.
언제: MVP 설계, 매 small-team product, 매 strong consistency 요구, 매 deployment simplicity 우선.
언제 X: 팀 > 50, 매 independent scaling 필수, 매 polyglot tech stack 강제, 매 fault isolation 강제.
❌ 안티패턴
Premature microservices: 매 100명 이하 팀이 매 microservices — 매 distributed monolith 직행.
Big ball of mud: 매 module boundary 없이 grow — 매 5년 후 rewrite.
Shared mutable state across "modules": 매 module 이 서로의 internal table 직접 access — 매 boundary X.
God service: 매 OrderService 가 매 모든 domain 호출 — 매 modularity X.
🧪 검증 / 중복
Verified (Fowler "MonolithFirst" 2015, Amazon Prime Video case study 2023, Shopify modular monolith talk 2021).