Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
sidebar_position: 4
pagination_next: reference/layers
---

# Excessive Entities

Feature-Sliced Design에서 `entities` Layer는 하위 Layer에 속하며, 재사용 가능한 도메인(비지니스) 로직을 담는 곳입니다.
이 Layer는 접근성이 높아서, `shared`를 제외한 거의 모든 Layer가 `entities`를 참조할 수 있어 접근 범위가 넓습니다.

다만 접근성이 높은 만큼 주의할 점도 있습니다.
`entities`에 코드가 추가/수정되거나 파일 경로가 바뀌면, 상위 Layer의 여러 Slice에서 그 변경을 함께 따라가야 할 수 있습니다.
그래서 리팩토링 비용이 커지기 전에, `entities`는 특히 경계와 역할을 더 명확하게 정의하고 관리하는 편이 좋습니다.

`entities`에 코드가 불필요하게 많이 쌓이면 보통 다음 문제가 같이 나타납니다.

- **경계가 모호해집니다**: “이 로직을 `entities`에 두는 게 맞나?” 같은 판단이 계속 필요해집니다.
- **결합도가 올라갑니다**: 여러 도메인이 서로 얽히면서 수정이 어려워집니다.
- **Import 딜레마가 생깁니다**: 코드가 동일 Layer의 다른 entity Slice로 흩어지면서, Import가 복잡해지고 선택이 어려워집니다.

## How to keep `entities` Layer clean

### 0. `entities` Layer 없이 시작하는 것도 가능합니다

`entities` Layer를 만들지 않으면 FSD가 아니라고 생각하기 쉽지만, 그렇지 않습니다.
애플리케이션에 `entities` Layer가 없어도 FSD 규칙이 깨지지 않습니다.
오히려 구조가 단순해지고, 나중에 규모가 커졌을 때 `entities`를 도입할 수 있도록 확장성을 확보할 수 있습니다.

예를 들어 애플리케이션이 **thin client**에 가깝다면, 대부분 `entities` Layer가 필요하지 않습니다.

:::info[What are thick and thin clients?]

thick client와 thin client의 구분은 “데이터 처리와 비즈니스 로직을 어디서 처리하느냐”를 기준으로 합니다.

- **thin client**: 대부분의 처리를 백엔드에서 수행합니다. 클라이언트에서는 비즈니스 로직을 최소화하고, 주로 백엔드와 데이터를 주고받습니다.
- **thick client**: 클라이언트에서 의미 있는 비즈니스 로직을 많이 처리합니다. 이런 경우 `entities` Layer를 두면 도메인 로직을 구조적으로 정리하기가 더 수월합니다.

이 구분은 딱 둘 중 하나로만 나뉘지 않습니다.
같은 애플리케이션이라도 일부는 thick client처럼, 다른 일부는 thin client처럼 동작할 수 있습니다.

:::

### 1. Slice를 처음부터 잘게 나누지 않습니다

FSD 2.1은 Slice를 미리 잘게 쪼개기보다, 필요해졌을 때 분리하는 접근을 권장합니다.
이 원칙은 `entities` Layer에도 그대로 적용됩니다.

처음에는 다음처럼 시작해도 됩니다.

1. page 또는 widget/feature Slice의 `model` Segment에 로직을 둡니다.
2. 요구사항이 어느 정도 안정되고, “이 로직은 여러 곳에서 재사용된다”가 분명해졌을 때 `entities`로 옮기는 리팩토링을 고려합니다.

여기서 중요한 점은 "언제 옮기느냐"입니다.
코드를 `entities`로 옮기는 시점이 늦을수록, 리팩토링 리스크가 줄어듭니다.
`entities`의 코드는 `shared`를 제외한 모든 Layer에서 쓰일 수 있어서, 변경이 여러 곳의 동작에 영향을 줄 수 있기 때문입니다.

### 2. 불필요한 Entities를 만들지 않습니다

비즈니스 로직이 있다고 해서 항상 entity를 만들어야 하는 것은 아닙니다.
먼저 `shared/api`의 타입을 활용하고, 로직은 현재 Slice의 `model` Segment에 두는 방식을 우선 고려합니다.

재사용 가능한 비즈니스 로직이 정말 필요하다면, 다음처럼 역할을 나누는 편이 좋습니다.

- 데이터 정의(예: 백엔드 응답 타입)는 `shared/api`에 둡니다.
- 재사용 로직은 entity Slice의 `model` Segment에 둡니다.


```plaintext
📂 entities
📂 order
📄 index.ts
📂 model
📄 apply-discount.ts // Business logic using OrderDto from shared/api
📂 shared
📂 api
📄 index.ts
📂 endpoints
📄 order.ts
```

### 3. CRUD는 `entities`에 두지 않는 편이 좋습니다

CRUD는 필수지만, 많은 경우 비즈니스 의미가 크지 않은 반복 코드가 됩니다.
이런 코드가 `entities`에 쌓이면 Layer가 지저분해지고, 중요한 로직이 눈에 잘 띄지 않게 됩니다.

```plaintext
📂 shared
📂 api
📄 client.ts
📄 index.ts
📂 endpoints
📄 order.ts // Contains all order-related CRUD operations
📄 products.ts
📄 cart.ts
```

대신 CRUD는 `shared/api`에 둡니다.

CRUD가 단순 호출 수준을 넘어, 예를 들어 여러 요청을 묶어서 일관성을 보장해야 하거나, 실패 시 rollback, transaction 같은 처리가 필요한 경우에는 `entities`가 맞는지 다시 판단할 수 있지만, 신중하게 적용하는 편이 좋습니다.

### 4. 인증 데이터는 `shared`에 둡니다

토큰이나 로그인 응답에 포함된 사용자 DTO처럼 인증 과정에서만 쓰이는 데이터는 `user` entity를 만들기보다 `shared`에 두는 편이 좋습니다.
이 데이터는 인증 Context에 종속적이며, 인증 범위를 벗어나 재사용될 가능성이 낮습니다.

- 로그인 응답은 상황에 따라 포함하는 정보가 달라질 수 있습니다(예: 공개/비공개 프로필).
- 이런 데이터를 entity로 올려버리면, 다른 곳에서 재사용하려다가 `shared`와 `entities` 사이 의존 관계가 꼬이거나,
cross-import를 표시하기 위한 `@x` 사용이 늘면서 구조가 더 복잡해질 수 있습니다.

따라서 인증과 직접 관련된 데이터는 `shared/auth` 또는 `shared/api`에 두는 방식을 권장합니다.


```plaintext
📂 shared
📂 auth
📄 use-auth.ts // authenticated user info or token
📄 index.ts
📂 api
📄 client.ts
📄 index.ts
📂 endpoints
📄 order.ts
```

인증 구현은 [Authentication 가이드](/docs/guides/examples/auth)를 참고하세요.

### 5. Cross-import를 최소화합니다

FSD는 `@x` 표기를 통해 cross-import를 허용하지만, 이 방식은 기술적 문제(예: 순환 의존)를 만들 수 있습니다.
이를 피하려면 entity를 서로 섞이지 않게 분리된 도메인 단위로 설계해 cross-import 자체가 필요 없도록 만드는 편이 좋습니다.

예를 들어 주문 아이템, 고객 정보처럼 항상 함께 움직이는 로직이 있다면, 이를 여러 entity로 쪼개기보다 order-info 같은 하나의 entity slice(모듈) 안에 캡슐화하는 방식이 더 낫습니다.


**Non-Isolated Business Context (Avoid):**

```plaintext
📂 entities
📂 order
📂 @x
📂 model
📂 order-item
📂 @x
📂 model
📂 order-customer-info
📂 @x
📂 model
```

**Isolated Business Context (Preferred):**

```plaintext
📂 entities
📂 order-info
📄 index.ts
📂 model
📄 order-info.ts
```

이렇게 하면 관련 코드가 한 곳에 모여 구조가 단순해지고, 강하게 결합된 로직이 여러 모듈로 흩어져 생기는 **변경 여파**도 줄일 수 있습니다.
또한 강하게 결합된 로직을 외부에서 수정/변경해야 하는 상황을 줄일 수 있습니다.