Application Service(應用服務)
是什麼?
Application Service 是 DDD 分層架構中**應用層(Application Layer)**的核心元件。它負責接收外部請求、協調 Domain Model 和 Infrastructure 完成一個完整的 Use Case,但不包含業務規則。
ℹ️薄服務原則
Application Service 必須是「薄」的——如果你發現 Service 裡有 if-else 在判斷業務規則,那些邏輯應該搬進 Domain Model。
核心觀念
Use Case 導向
每個公開方法對應一個用例。方法名稱反映業務操作,例如 PlaceOrder、CancelOrder,而不是 UpdateOrderStatus。
協調者,不是執行者
Application Service 的工作模式是固定的:
- 從 Repository 取出 Aggregate
- 呼叫 Aggregate 上的業務方法
- 把結果存回 Repository
- 發送 Domain Event(如果有的話)
交易邊界
Application Service 通常是交易(Transaction)的起點和終點,確保一個 Use Case 的所有操作要嘛全成功、要嘛全失敗。
常見誤區
⚠️Fat Service 壞味道
把業務邏輯塞進 Application Service 是最常見的錯誤。當你看到 Service 裡有「如果訂單金額大於 1000 就打九折」這種邏輯,它應該在 Domain Model 裡,不是在 Service 裡。
協調流程
接收請求
從 Controller/Handler 接收 Command 或 DTO
載入 Aggregate
透過 Repository 取出需要操作的領域物件
執行業務操作
呼叫 Aggregate 上的方法(邏輯在 Domain 裡)
持久化結果
透過 Repository 儲存變更後的 Aggregate
發布事件
發送 Domain Event 通知其他 Bounded Context
程式碼範例
C#
// Application Service — 薄,只做協調
public class OrderApplicationService
{
private readonly IOrderRepository _orderRepo;
private readonly IProductRepository _productRepo;
private readonly IDomainEventPublisher _eventPublisher;
public OrderApplicationService(
IOrderRepository orderRepo,
IProductRepository productRepo,
IDomainEventPublisher eventPublisher)
{
_orderRepo = orderRepo;
_productRepo = productRepo;
_eventPublisher = eventPublisher;
}
// 一個方法 = 一個 Use Case
public void PlaceOrder(PlaceOrderCommand command)
{
// 1. 載入需要的領域物件
var product = _productRepo.GetById(command.ProductId);
// 2. 呼叫 Domain Model 的業務方法(邏輯在 Order 裡)
var order = Order.Create(
customerId: command.CustomerId,
product: product,
quantity: command.Quantity
);
// 3. 持久化
_orderRepo.Save(order);
// 4. 發布事件
_eventPublisher.Publish(new OrderPlacedEvent(order.Id));
}
public void CancelOrder(Guid orderId, string reason)
{
var order = _orderRepo.GetById(orderId);
order.Cancel(reason); // 業務邏輯在 Aggregate 裡
_orderRepo.Save(order);
_eventPublisher.Publish(new OrderCancelledEvent(orderId, reason));
}
}
// Domain Model — 業務邏輯在這裡
public class Order
{
public static Order Create(Guid customerId, Product product, int quantity)
{
if (quantity <= 0)
throw new DomainException("Quantity must be positive");
if (!product.IsAvailable)
throw new DomainException("Product is not available");
var total = product.Price * quantity;
if (total > 1000) total *= 0.9m; // 折扣邏輯在 Domain 裡
return new Order(customerId, product.Id, quantity, total);
}
}TypeScript
// Application Service
class OrderApplicationService {
constructor(
private orderRepo: OrderRepository,
private productRepo: ProductRepository,
private eventPublisher: DomainEventPublisher
) {}
async placeOrder(command: PlaceOrderCommand): Promise<void> {
const product = await this.productRepo.getById(command.productId);
// 業務邏輯在 Order.create 裡
const order = Order.create(
command.customerId,
product,
command.quantity
);
await this.orderRepo.save(order);
await this.eventPublisher.publish(new OrderPlacedEvent(order.id));
}
async cancelOrder(orderId: string, reason: string): Promise<void> {
const order = await this.orderRepo.getById(orderId);
order.cancel(reason); // 業務邏輯在 Aggregate 裡
await this.orderRepo.save(order);
await this.eventPublisher.publish(new OrderCancelledEvent(orderId, reason));
}
}Python
class OrderApplicationService:
def __init__(
self,
order_repo: OrderRepository,
product_repo: ProductRepository,
event_publisher: DomainEventPublisher,
):
self._order_repo = order_repo
self._product_repo = product_repo
self._event_publisher = event_publisher
def place_order(self, command: PlaceOrderCommand) -> None:
product = self._product_repo.get_by_id(command.product_id)
# 業務邏輯在 Order.create 裡
order = Order.create(
customer_id=command.customer_id,
product=product,
quantity=command.quantity,
)
self._order_repo.save(order)
self._event_publisher.publish(OrderPlacedEvent(order.id))
def cancel_order(self, order_id: str, reason: str) -> None:
order = self._order_repo.get_by_id(order_id)
order.cancel(reason) # 業務邏輯在 Aggregate 裡
self._order_repo.save(order)
self._event_publisher.publish(OrderCancelledEvent(order_id, reason))結構圖
此圖展示 Application Service 的「導演」角色:它接收 Controller 的請求,從 Repository 載入 Aggregate,呼叫 Aggregate 的業務方法,儲存結果,發布事件。注意 Application Service 不直接碰 Database,也不包含業務邏輯——它只做串接和協調。
Application Service vs Domain Service vs Infrastructure Service
| 類型 | 職責 | 放在哪一層 | 範例 |
|------|------|-----------|------|
| Application Service | 協調用例流程:取出→操作→儲存→發事件 | Application 層 | PlaceOrderHandler |
| Domain Service | 封裝跨 Aggregate 的業務規則,不屬於任何單一 Entity | Domain 層 | TransferMoneyService(從帳戶 A 轉到帳戶 B,涉及兩個 Aggregate) |
| Infrastructure Service | 封裝基礎設施操作 | Infrastructure 層 | SmtpEmailService、S3FileStorage |
判斷邏輯是否該在 Application Service 裡的方法:拿掉所有 if-else 後,Service 的方法是否還能正常運作?如果可以,那些 if-else 就是業務邏輯,搬到 Domain Model 或 Domain Service。
實戰補充
ℹ️資深開發者心得
判斷 Application Service 是否太胖的簡單方法:如果拿掉所有 if-else 後 Service 還能運作,那些 if-else 就應該在 Domain 裡。 Application Service 的流程應該是線性的:取出、操作、儲存、發事件。
💡與 Domain Service 的區別
Application Service 協調用例流程,Domain Service 封裝跨 Aggregate 的業務規則。如果邏輯需要多個 Aggregate 協作且包含業務判斷,那是 Domain Service 的職責,不是 Application Service。
理解測驗
🤔 Application Service 的核心職責是什麼?
🤔 如果你在 Application Service 裡看到「如果 VIP 客戶則免運費」這段邏輯,正確的做法是?
🤔 Application Service 和 Domain Service 的關鍵差異是?
重點整理
💡一句話記住
口訣:「取出、操作、儲存、發事件」—— Application Service 的四步萬能公式,多一步就是越權。
| 概念 | 說明 | |------|------| | 薄服務 | Service 只做協調,業務邏輯在 Domain Model | | Use Case 導向 | 每個公開方法對應一個業務操作 | | 固定模式 | 取出 → 操作 → 儲存 → 發事件 | | 交易邊界 | Service 是 Transaction 的起點和終點 | | 判斷標準 | 拿掉 if-else 後 Service 還能運作 → 那些邏輯應搬到 Domain |