init: 클린 기반 auth 서버 설계
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# port abstraction 예시
|
||||
|
||||
## 좋은 예시 1: outbound port를 application이 소유
|
||||
|
||||
```java
|
||||
public interface UserReader {
|
||||
Optional<User> findByEmail(UserEmail email);
|
||||
Optional<User> findById(UserId userId);
|
||||
}
|
||||
|
||||
@Repository
|
||||
public class JpaUserReader implements UserReader {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- application이 persistence 기술을 모른다
|
||||
- 코어가 필요한 조회 능력만 계약으로 드러난다
|
||||
- adapter만 JPA를 안다
|
||||
|
||||
## 좋은 예시 2: inbound port를 use case 계약으로 사용
|
||||
|
||||
```java
|
||||
public interface LoginUseCase {
|
||||
LoginResult login(LoginCommand command);
|
||||
}
|
||||
|
||||
@RestController
|
||||
class LoginController {
|
||||
private final LoginUseCase loginUseCase;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- controller가 구현체보다 use case 계약에 의존한다
|
||||
- HTTP 세부와 비즈니스 흐름이 분리된다
|
||||
|
||||
## 좋은 예시 3: external API 경계 포트
|
||||
|
||||
```java
|
||||
public interface TokenSigner {
|
||||
Signature sign(SigningRequest request);
|
||||
}
|
||||
|
||||
public class VaultTokenSigner implements TokenSigner {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- 포트는 “서명한다”는 능력만 표현한다
|
||||
- HTTP, JSON, Vault path/header는 adapter 구현으로 숨긴다
|
||||
|
||||
## 좋은 예시 4: 하나의 포트에 여러 adapter 가능
|
||||
|
||||
```java
|
||||
public interface RateRepository {
|
||||
BigDecimal findDiscountRate(Money amount);
|
||||
}
|
||||
|
||||
public class InMemoryRateRepository implements RateRepository { ... }
|
||||
|
||||
public class JdbcRateRepository implements RateRepository { ... }
|
||||
```
|
||||
|
||||
**왜 좋은가:**
|
||||
|
||||
- 테스트와 운영 구현이 같은 계약을 공유한다
|
||||
- 포트는 기술 수와 무관하게 같은 대화를 표현한다
|
||||
|
||||
## 나쁜 예시 1: 기술 타입이 새는 포트
|
||||
|
||||
```java
|
||||
public interface UserApiPort {
|
||||
ResponseEntity<String> getUser(String id);
|
||||
}
|
||||
```
|
||||
|
||||
**문제:**
|
||||
|
||||
- HTTP 세부가 코어 계약으로 올라온다
|
||||
- 비즈니스 의미가 아니라 transport 형식이 중심이 된다
|
||||
|
||||
## 나쁜 예시 2: adapter 편의 중심 포트
|
||||
|
||||
```java
|
||||
public interface DatabasePort {
|
||||
String query(String sql);
|
||||
}
|
||||
```
|
||||
|
||||
**문제:**
|
||||
|
||||
- 코어가 SQL/DB 기술 세부를 알게 된다
|
||||
- “무엇을 원하나”가 아니라 “어떻게 하냐”를 말한다
|
||||
|
||||
## 나쁜 예시 3: 너무 범용적인 outbound port
|
||||
|
||||
```java
|
||||
public interface ExternalSystemPort {
|
||||
Object execute(Object input);
|
||||
}
|
||||
```
|
||||
|
||||
**문제:**
|
||||
|
||||
- 역할이 불명확하다
|
||||
- 타입 안정성과 계약 의미가 없다
|
||||
- 여러 외부 시스템 책임을 한 곳에 섞기 쉽다
|
||||
|
||||
## 나쁜 예시 4: 내부 helper까지 포트화
|
||||
|
||||
```java
|
||||
public interface EmailNormalizerPort {
|
||||
String normalize(String raw);
|
||||
}
|
||||
```
|
||||
|
||||
**문제:**
|
||||
|
||||
- 외부 경계가 아니라 내부 로직 detail이다
|
||||
- 포트 추상화 비용이 이득보다 크다
|
||||
Reference in New Issue
Block a user