숏컷 코드
한 서비스가 소유해야 할 것
-> 자기 데이터
-> 자기 규칙
-> 자기 상태 변화
남의 데이터까지 직접 고치기 시작
-> 경계가 흐려짐문법과 예시
서비스 경계는 API 개수보다 "누가 최종 책임을 지는가"로 정한다
같은 데이터를 여러 서비스가 같이 만지기 시작하면 수정 지점은 늘고 버그는 추적하기 어려워집니다. 그래서 경계는 단순히 메서드를 어디에 둘지가 아니라, "이 상태 변화의 최종 책임자가 누구인가"를 먼저 정하는 문제입니다.
예시
- InventoryService: 아이템 소유/추가/제거 책임
- QuestService: 퀘스트 진행 책임
- SaveService: 저장 직렬화 책임ownership이 선명하면 읽기와 테스트가 쉬워진다
아이템 지급은 InventoryService, 퀘스트 진행은 QuestService처럼 ownership이 선명하면 호출부는 "누구에게 요청해야 하는지"를 바로 알 수 있습니다.
테스트도 각 서비스의 입력과 출력만 검증하면 되므로 경계가 명확해집니다.
가장 흔한 문제는 "편해서 직접 건드리기"다
처음엔 편해서 QuestService가 InventoryService.items를 직접 수정하고, UIService가 PlayerStats.hp를 바로 바꾸기 시작합니다.
이 순간 service boundary는 사실상 무너집니다. 읽는 사람은 어느 변경이 어디서 끝나는지 확신할 수 없게 됩니다.
경계를 넘을 때는 요청과 이벤트를 구분하는 편이 좋다
상태를 직접 바꾸는 대신 "요청"을 보내거나, 상태 변화 뒤 "이벤트"를 발행하면 경계가 더 선명해집니다. 즉 다른 서비스의 내부 컬렉션이나 필드를 직접 만지는 대신, 그 서비스가 제공하는 명시적 API를 통하게 하는 편이 안전합니다.
ownership을 정할 때 핵심
| 상황 | 더 자연스러운 선택 |
|---|---|
| 특정 상태의 최종 진실 공급자가 하나여야 할 때 | ownership 명확히 고정 |
| 다른 서비스 내부 컬렉션을 자주 직접 수정할 때 | boundary 재설계 |
| 변경 요청과 결과 알림을 나눌 수 있을 때 | 요청 API + 이벤트 구조 |
| 서비스 책임이 UI, 저장, 도메인 규칙까지 퍼질 때 | 역할 다시 분리 |
| 누가 상태를 소유하는지 설명이 안 될 때 | 먼저 ownership 문장부터 작성 |
특이 케이스와 주의할 점
흔한 실패는 "결국 같은 팀 코드니까"라는 이유로 서로의 내부 상태를 직접 건드리는 것입니다. 이 방식은 단기 속도는 나지만, 저장/복구/동기화가 들어오는 순간 경계 버그가 한꺼번에 터집니다.
실패 예시
- RewardService가 InventoryService 내부 리스트를 직접 append
- QuestService가 PlayerStats.currentHp를 직접 수정
결과
- 상태 변화의 최종 책임자가 모호해짐
- 저장/복구 지점도 복잡해짐