函数式领域驱动设计(Functional Domain-Driven Design,简称 FDDD)是将函数式编程范式与领域驱动设计方法论相结合的软件架构方法。这种融合旨在利用函数式编程的数学严谨性、不可变性和可组合性,同时保持领域驱动设计对业务领域的深度建模能力。
函数式编程与领域驱动设计看似来自不同的范式世界——前者源于数学和计算机科学的理论根基,后者诞生于企业软件开发的实践需求。然而,两者在核心思想上有着深刻的共鸣:都强调对概念的精确表达、对副作用的严格控制、以及对系统边界的清晰划分。
本文将深入探讨函数式领域驱动设计的理论基础、核心概念、实践方法和实施策略,帮助开发团队在复杂业务场景下构建高内聚、低耦合、易于演进的软件系统。
函数式编程(Functional Programming,FP)是一种以数学函数为核心的编程范式,其核心特征包括:
纯函数是指对于相同的输入,始终产生相同的输出,且不会产生任何副作用的函数。纯函数具有以下特性:
引用透明性(Referential Transparency)
引用透明性意味着函数调用可以被其结果直接替换,而不会影响程序的行为。这一特性使得代码更易于推理、测试和优化。
// 纯函数示例:计算订单总价
val calculateTotal: (Order) => Money = order => {
order.items.foldLeft(Money.zero) { (total, item) =>
total + (item.price * item.quantity)
}
}
// 相同的输入总是产生相同的输出
val order = Order(items = List(Item("A", Money(100), 2)))
calculateTotal(order) // 总是返回 Money(200)
无副作用(No Side Effects)
纯函数不修改外部状态,不执行 I/O 操作,不改变全局变量。这种特性使得函数的行为完全可预测,便于并行执行和缓存优化。
不可变性是函数式编程的基石。在不可变数据模型中,数据一旦创建就不能被修改,任何"修改"操作都会返回一个新的数据实例。
// Kotlin 中的不可变数据类
data class Order(
val id: OrderId,
val customerId: CustomerId,
val items: List<OrderItem>,
val status: OrderStatus,
val createdAt: Instant
)
// 修改订单状态返回新实例,原实例保持不变
fun Order.confirm(): Order =
if (this.status == OrderStatus.PENDING) {
this.copy(status = OrderStatus.CONFIRMED)
} else {
throw IllegalStateException("Cannot confirm order in status: ${this.status}")
}
不可变性的优势包括:
高阶函数是指接受函数作为参数或返回函数的函数。函数组合则是将多个简单函数组合成复杂函数的技术。
// TypeScript:函数组合示例
const pipe = <T>(...fns: Array<(arg: T) => T>) =>
(value: T) => fns.reduce((acc, fn) => fn(acc), value);
// 领域验证管道
const validateOrder = pipe(
validateItemsNotEmpty,
validatePricesPositive,
validateCustomerActive,
validateInventoryAvailable
);
const result = validateOrder(order);
代数数据类型是函数式编程中描述数据结构的重要工具,主要包括:
积类型(Product Types)
积类型表示"与"关系,对应面向对象中的类或结构体。例如,一个订单包含订单ID、客户ID、订单项列表等字段。
// F# 中的记录类型(积类型)
type Order = {
Id: OrderId
CustomerId: CustomerId
Items: OrderItem list
Status: OrderStatus
CreatedAt: DateTime
}
和类型(Sum Types)
和类型表示"或"关系,用于建模互斥的状态或选择。例如,支付结果可能是成功、失败或待处理。
// Rust 中的枚举(和类型)
enum PaymentResult {
Success { transactionId: String, amount: Money },
Failure { reason: PaymentFailureReason },
Pending { estimatedCompletion: DateTime },
}
// 使用模式匹配处理所有情况
fn process_payment_result(result: PaymentResult) -> String {
match result {
PaymentResult::Success { transactionId, amount } =>
format!("Payment {} completed: {}", transactionId, amount),
PaymentResult::Failure { reason } =>
format!("Payment failed: {:?}", reason),
PaymentResult::Pending { estimatedCompletion } =>
format!("Payment pending, expected by {}", estimatedCompletion),
}
}
领域驱动设计(Domain-Driven Design,DDD)是由 Eric Evans 提出的一种软件开发方法,强调以领域为核心进行软件设计和开发。
战略设计关注系统的宏观架构和领域边界划分:
限界上下文(Bounded Context)
限界上下文是领域模型的显式边界,在边界内,领域术语具有唯一、明确的含义。不同的限界上下文可以独立演进,通过上下文映射(Context Mapping)进行集成。
┌─────────────────────────────────────────────────────────────┐
│ 电商系统 │
├─────────────────┬─────────────────┬─────────────────────────┤
│ 订单上下文 │ 库存上下文 │ 支付上下文 │
│ (Order Context) │ (Inventory │ (Payment Context) │
│ │ Context) │ │
│ - Order │ - Stock │ - Payment │
│ - OrderItem │ - Reservation │ - Refund │
│ - OrderStatus │ - Warehouse │ - PaymentMethod │
└─────────────────┴─────────────────┴─────────────────────────┘
通用语言(Ubiquitous Language)
通用语言是领域专家和开发团队共享的、精确的领域术语体系。它贯穿于代码、文档、对话和图表中,确保所有人对领域概念的理解一致。
战术设计提供了构建领域模型的具体模式:
实体(Entity)
实体是具有唯一标识的领域对象,其标识在整个生命周期中保持不变,而属性可以发生变化。
值对象(Value Object)
值对象没有概念上的标识,通过其属性值来定义相等性。值对象应该是不可变的,可以安全地在多个实体间共享。
// Java:值对象示例
public record Money(BigDecimal amount, Currency currency) {
public Money {
Objects.requireNonNull(amount, "Amount cannot be null");
Objects.requireNonNull(currency, "Currency cannot be null");
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
}
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new CurrencyMismatchException(this.currency, other.currency);
}
return new Money(this.amount.add(other.amount), this.currency);
}
public Money multiply(int factor) {
return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency);
}
}
聚合(Aggregate)
聚合是一组相关领域对象的集群,将实体和值对象封装在一致性边界内。聚合根(Aggregate Root)是聚合的入口点,外部对象只能通过聚合根访问聚合内部的对象。
领域服务(Domain Service)
领域服务封装了不适合放在实体或值对象中的领域逻辑,通常涉及多个领域对象的操作或需要访问基础设施。
领域事件(Domain Event)
领域事件表示领域中发生的有意义的事情,用于在限界上下文之间传播状态变化,实现最终一致性。
// C#:领域事件示例
public record OrderPlaced(
OrderId OrderId,
CustomerId CustomerId,
IReadOnlyList<OrderItem> Items,
Money TotalAmount,
DateTime OccurredOn
) : DomainEvent;
public record PaymentCompleted(
OrderId OrderId,
TransactionId TransactionId,
Money Amount,
DateTime CompletedAt
) : DomainEvent;
函数式编程的不可变性与DDD值对象的设计理念高度契合。值对象本身就应该是不可变的,一旦创建,其属性就不应改变。
在函数式编程语言中,值对象可以简洁地表达为记录类型(Record)或数据类(Data Class):
// Scala:值对象与代数数据类型的结合
sealed trait OrderStatus
object OrderStatus {
case object Pending extends OrderStatus
case object Confirmed extends OrderStatus
case object Shipped extends OrderStatus
case object Delivered extends OrderStatus
case object Cancelled extends OrderStatus
}
case class OrderItem(
productId: ProductId,
productName: String,
unitPrice: Money,
quantity: Quantity
) {
def subtotal: Money = unitPrice * quantity.value
}
case class Quantity(value: Int) {
require(value > 0, "Quantity must be positive")
require(value <= 1000, "Quantity cannot exceed 1000")
}
在函数式DDD中,实体的状态变化不是通过修改属性实现的,而是通过返回新实例的方式:
// Scala:实体的函数式状态转换
case class Order(
id: OrderId,
customerId: CustomerId,
items: NonEmptyList[OrderItem],
status: OrderStatus,
shippingAddress: Address,
createdAt: Instant,
confirmedAt: Option[Instant] = None,
shippedAt: Option[Instant] = None
) {
def confirm(at: Instant): Either[OrderError, Order] = status match {
case OrderStatus.Pending =>
Right(this.copy(status = OrderStatus.Confirmed, confirmedAt = Some(at)))
case _ =>
Left(OrderError.InvalidStatusTransition(status, OrderStatus.Confirmed))
}
def ship(at: Instant, trackingNumber: TrackingNumber): Either[OrderError, Order] = status match {
case OrderStatus.Confirmed =>
Right(this.copy(
status = OrderStatus.Shipped,
shippedAt = Some(at)
))
case _ =>
Left(OrderError.InvalidStatusTransition(status, OrderStatus.Shipped))
}
def totalAmount: Money = items.map(_.subtotal).reduce(_ + _)
}
这种实现方式的优势:
代数数据类型为领域建模提供了强大的表达能力,特别适合描述业务规则和业务状态。
和类型可以精确地表达互斥的业务状态,避免使用布尔标志或空值带来的歧义:
// Kotlin:用密封类和和类型建模支付状态
sealed class PaymentStatus {
abstract val orderId: OrderId
data class Pending(
override val orderId: OrderId,
val initiatedAt: Instant,
val paymentMethod: PaymentMethod
) : PaymentStatus()
data class Authorized(
override val orderId: OrderId,
val authorizedAt: Instant,
val authorizationCode: String,
val authorizedAmount: Money
) : PaymentStatus()
data class Captured(
override val orderId: OrderId,
val capturedAt: Instant,
val transactionId: TransactionId,
val capturedAmount: Money
) : PaymentStatus()
data class Failed(
override val orderId: OrderId,
val failedAt: Instant,
val reason: PaymentFailureReason,
val retryable: Boolean
) : PaymentStatus()
data class Refunded(
override val orderId: OrderId,
val refundedAt: Instant,
val refundTransactionId: TransactionId,
val refundAmount: Money
) : PaymentStatus()
}
// 使用模式匹配处理不同状态
fun processPayment(status: PaymentStatus): String = when (status) {
is PaymentStatus.Pending -> "等待支付确认"
is PaymentStatus.Authorized -> "已授权,等待扣款"
is PaymentStatus.Captured -> "支付完成"
is PaymentStatus.Failed -> if (status.retryable) "支付失败,可重试" else "支付失败"
is PaymentStatus.Refunded -> "已退款"
}
积类型用于构建复杂的领域对象,将相关的属性组合在一起:
// F#:积类型建模地址
type Address = {
Street: string
City: string
State: string
ZipCode: ZipCode
Country: CountryCode
}
and ZipCode = private ZipCode of string
module ZipCode =
let create (value: string) : Result<ZipCode, string> =
if System.Text.RegularExpressions.Regex.IsMatch(value, @"^\d{5}(-\d{4})?$")
then Ok (ZipCode value)
else Error "Invalid ZIP code format"
let value (ZipCode v) = v
函数式编程的函数组合能力非常适合构建领域操作的管道。
使用函数组合构建验证管道,将多个独立的验证规则组合成完整的验证流程:
// TypeScript:函数式验证管道
import { Either, left, right, chain, map } from 'fp-ts/Either';
import { pipe } from 'fp-ts/function';
// 领域错误类型
interface ValidationError {
field: string;
message: string;
}
// 验证函数类型
type Validator<T> = (value: T) => Either<ValidationError[], T>;
// 组合验证器
const combineValidators = <T>(...validators: Validator<T>[]): Validator<T> =>
(value: T) => {
const results = validators.map(v => v(value));
const errors = results.filter(r => r._tag === 'Left').flatMap(r =>
r._tag === 'Left' ? r.left : []
);
return errors.length > 0 ? left(errors) : right(value);
};
// 具体验证规则
const validateOrderItemsNotEmpty: Validator<Order> = (order) =>
order.items.length === 0
? left([{ field: 'items', message: 'Order must contain at least one item' }])
: right(order);
const validateTotalAmountPositive: Validator<Order> = (order) =>
order.totalAmount.value <= 0
? left([{ field: 'totalAmount', message: 'Total amount must be positive' }])
: right(order);
const validateCustomerNotBlocked: Validator<Order> = (order) =>
order.customerStatus === 'BLOCKED'
? left([{ field: 'customer', message: 'Customer is blocked' }])
: right(order);
// 完整的订单验证器
const validateOrder = combineValidators(
validateOrderItemsNotEmpty,
validateTotalAmountPositive,
validateCustomerNotBlocked
);
// 使用
const result = validateOrder(order);
将领域操作建模为函数管道,每个步骤的输出作为下一步的输入:
// Scala:领域操作管道
import cats.data.EitherT
import cats.effect.IO
type DomainResult[A] = Either[DomainError, A]
type DomainIO[A] = EitherT[IO, DomainError, A]
class OrderService(
inventoryService: InventoryService,
pricingService: PricingService,
paymentService: PaymentService,
orderRepository: OrderRepository
) {
def placeOrder(
customerId: CustomerId,
items: List[OrderLineItem],
shippingAddress: Address
): DomainIO[Order] = {
for {
// 1. 验证库存
_ <- EitherT(inventoryService.checkAvailability(items))
// 2. 计算价格
pricedItems <- EitherT(pricingService.calculatePrices(items))
// 3. 创建订单
order = Order.create(customerId, pricedItems, shippingAddress)
// 4. 预留库存
_ <- EitherT(inventoryService.reserveStock(order.id, items))
// 5. 保存订单
savedOrder <- EitherT(orderRepository.save(order))
} yield savedOrder
}.leftMap { error =>
// 错误处理:根据错误类型决定是否回滚库存
error match {
case DomainError.PaymentFailed(_) =>
inventoryService.releaseReservation(order.id).unsafeRunSync()
case _ => ()
}
error
}
}
函数式编程强调将副作用隔离在系统的边界,领域核心保持纯函数特性。
采用"函数式核心,指令式外壳"(Functional Core, Imperative Shell)的架构模式:
// Kotlin:纯函数核心
object OrderDomain {
// 纯函数:计算订单总价
fun calculateTotal(items: List<OrderItem>): Money =
items.fold(Money.zero(Currency.CNY)) { total, item ->
total + item.unitPrice * item.quantity
}
// 纯函数:应用折扣
fun applyDiscount(total: Money, discount: Discount): Money = when (discount) {
is Discount.FixedAmount -> maxOf(Money.zero(total.currency), total - discount.amount)
is Discount.Percentage -> total * (1 - discount.percent / 100)
is Discount.BuyXGetY -> calculateBuyXGetYDiscount(total, discount)
}
// 纯函数:验证订单
fun validateOrder(order: Order): ValidationResult<Order> {
val errors = mutableListOf<ValidationError>()
if (order.items.isEmpty()) {
errors.add(ValidationError("items", "Order must have at least one item"))
}
if (order.totalAmount <= Money.zero(order.totalAmount.currency)) {
errors.add(ValidationError("totalAmount", "Total amount must be positive"))
}
return if (errors.isEmpty()) ValidationResult.Success(order)
else ValidationResult.Failure(errors)
}
}
// 指令式外壳:处理副作用
class OrderApplicationService(
private val orderRepository: OrderRepository,
private val eventPublisher: EventPublisher,
private val pricingService: PricingService
) {
suspend fun createOrder(command: CreateOrderCommand): Result<Order> = try {
// 1. 获取数据(副作用)
val customer = customerRepository.findById(command.customerId)
?: return Result.failure(CustomerNotFoundException(command.customerId))
// 2. 构建领域对象
val items = command.items.map { item ->
val price = pricingService.getCurrentPrice(item.productId)
OrderItem(item.productId, item.quantity, price)
}
// 3. 纯函数核心逻辑
val order = Order.create(
customerId = command.customerId,
items = items,
shippingAddress = command.shippingAddress
)
// 4. 验证(纯函数)
when (val validation = OrderDomain.validateOrder(order)) {
is ValidationResult.Success -> {
// 5. 保存(副作用)
orderRepository.save(order)
// 6. 发布事件(副作用)
eventPublisher.publish(OrderCreatedEvent(order.id, order.customerId))
Result.success(order)
}
is ValidationResult.Failure ->
Result.failure(ValidationException(validation.errors))
}
} catch (e: Exception) {
Result.failure(e)
}
}
使用 Effect 系统(如 Cats Effect、ZIO)来显式追踪和管理副作用:
// Scala:使用 Cats Effect 管理副作用
import cats.effect.{IO, Ref}
import cats.syntax.all._
trait OrderRepository {
def findById(id: OrderId): IO[Option[Order]]
def save(order: Order): IO[Order]
def update(order: Order): IO[Order]
}
trait EventPublisher {
def publish(event: DomainEvent): IO[Unit]
}
class OrderServiceImpl(
repository: OrderRepository,
publisher: EventPublisher
) {
def confirmOrder(orderId: OrderId): IO[Either[DomainError, Order]] = {
for {
// 查询订单(副作用)
maybeOrder <- repository.findById(orderId)
// 纯函数逻辑
result <- maybeOrder match {
case None => IO.pure(Left(DomainError.OrderNotFound(orderId)))
case Some(order) =>
IO.realTimeInstant.flatMap { now =>
order.confirm(now) match {
case Left(error) => IO.pure(Left(error))
case Right(confirmedOrder) =>
for {
// 保存(副作用)
saved <- repository.save(confirmedOrder)
// 发布事件(副作用)
_ <- publisher.publish(
OrderConfirmedEvent(saved.id, saved.customerId, now)
)
} yield Right(saved)
}
}
}
} yield result
}
}
在函数式DDD中,聚合的设计需要平衡不变性保证和一致性边界。
聚合根作为一致性边界内的入口点,负责维护聚合的不变式:
// Rust:函数式聚合实现
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Order {
id: OrderId,
customer_id: CustomerId,
items: Vec<OrderItem>,
status: OrderStatus,
shipping_address: Address,
created_at: DateTime<Utc>,
version: u64, // 乐观锁版本号
}
#[derive(Debug, Clone)]
pub struct OrderItem {
product_id: ProductId,
product_name: String,
unit_price: Money,
quantity: u32,
}
impl Order {
// 工厂方法
pub fn create(
id: OrderId,
customer_id: CustomerId,
items: Vec<OrderItem>,
shipping_address: Address,
) -> Result<Self, OrderError> {
if items.is_empty() {
return Err(OrderError::EmptyOrder);
}
// 验证所有项目
for item in &items {
if item.quantity == 0 {
return Err(OrderError::InvalidQuantity(item.product_id.clone()));
}
}
Ok(Order {
id,
customer_id,
items,
status: OrderStatus::Pending,
shipping_address,
created_at: Utc::now(),
version: 1,
})
}
// 纯函数式状态转换
pub fn confirm(mut self) -> Result<Self, OrderError> {
match self.status {
OrderStatus::Pending => {
self.status = OrderStatus::Confirmed;
self.version += 1;
Ok(self)
}
_ => Err(OrderError::InvalidStatusTransition {
from: self.status,
to: OrderStatus::Confirmed,
}),
}
}
pub fn add_item(mut self, item: OrderItem) -> Result<Self, OrderError> {
match self.status {
OrderStatus::Pending => {
// 检查是否已存在相同商品
if let Some(existing) = self.items.iter_mut()
.find(|i| i.product_id == item.product_id) {
existing.quantity += item.quantity;
} else {
self.items.push(item);
}
self.version += 1;
Ok(self)
}
_ => Err(OrderError::CannotModifyConfirmedOrder),
}
}
pub fn total_amount(&self) -> Money {
self.items.iter()
.map(|item| item.unit_price * item.quantity)
.fold(Money::zero(Currency::CNY), |acc, m| acc + m)
}
}
在函数式实现中,领域不变式通过类型系统和构造函数来保证:
// Scala:使用智能构造器维护不变式
sealed abstract case class EmailAddress private (value: String)
object EmailAddress {
private val EmailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$".r
def create(value: String): Either[ValidationError, EmailAddress] = {
if (value.isEmpty) {
Left(ValidationError("Email cannot be empty"))
} else if (!EmailRegex.matches(value)) {
Left(ValidationError(s"Invalid email format: $value"))
} else {
Right(new EmailAddress(value) {})
}
}
// 不安全构造,仅在已知有效时使用
def unsafeCreate(value: String): EmailAddress =
create(value).getOrElse(throw new IllegalArgumentException(s"Invalid email: $value"))
}
// 使用示例
val emailResult = EmailAddress.create("user@example.com")
emailResult match {
case Right(email) => println(s"Valid email: ${email.value}")
case Left(error) => println(s"Invalid: ${error.message}")
}
领域事件在函数式DDD中扮演着重要角色,用于实现限界上下文间的松耦合通信。
// Kotlin:领域事件的函数式定义
sealed interface DomainEvent {
val eventId: EventId
val occurredOn: Instant
val aggregateId: String
}
data class OrderPlacedEvent(
override val eventId: EventId = EventId.generate(),
override val occurredOn: Instant = Instant.now(),
override val aggregateId: String,
val customerId: String,
val items: List<OrderItemSnapshot>,
val totalAmount: MoneySnapshot,
val shippingAddress: AddressSnapshot
) : DomainEvent
data class OrderConfirmedEvent(
override val eventId: EventId = EventId.generate(),
override val occurredOn: Instant = Instant.now(),
override val aggregateId: String,
val confirmedAt: Instant
) : DomainEvent
// 事件发布器接口
fun interface EventPublisher {
fun publish(event: DomainEvent): Result<Unit>
}
// 函数式事件处理器
typealias EventHandler<T> = (T) -> Result<Unit>
class EventDispatcher {
private val handlers = mutableMapOf<Class<out DomainEvent>, MutableList<EventHandler<DomainEvent>>>()
inline fun <reified T : DomainEvent> register(handler: EventHandler<T>) {
@Suppress("UNCHECKED_CAST")
handlers.getOrPut(T::class.java) { mutableListOf() }
.add(handler as EventHandler<DomainEvent>)
}
fun dispatch(event: DomainEvent): Result<Unit> {
val eventHandlers = handlers[event::class.java] ?: return Result.success(Unit)
return eventHandlers.fold(Result.success(Unit)) { acc, handler ->
acc.flatMap { handler(event) }
}
}
}
事件溯源是一种将系统状态存储为事件序列的模式,与函数式编程天然契合:
// Scala:事件溯源实现
sealed trait OrderEvent {
def orderId: OrderId
def occurredAt: Instant
}
object OrderEvent {
case class OrderCreated(
orderId: OrderId,
customerId: CustomerId,
items: List[OrderItem],
shippingAddress: Address,
occurredAt: Instant
) extends OrderEvent
case class ItemAdded(
orderId: OrderId,
item: OrderItem,
occurredAt: Instant
) extends OrderEvent
case class OrderConfirmed(
orderId: OrderId,
confirmedAt: Instant,
occurredAt: Instant
) extends OrderEvent
case class OrderShipped(
orderId: OrderId,
trackingNumber: TrackingNumber,
shippedAt: Instant,
occurredAt: Instant
) extends OrderEvent
}
// 状态折叠函数(纯函数)
object OrderState {
def empty(orderId: OrderId): Order = ??? // 返回空订单
def applyEvent(order: Order, event: OrderEvent): Order = event match {
case e: OrderEvent.OrderCreated =>
Order(
id = e.orderId,
customerId = e.customerId,
items = e.items,
status = OrderStatus.Pending,
shippingAddress = e.shippingAddress,
createdAt = e.occurredAt
)
case e: OrderEvent.ItemAdded =>
order.copy(items = order.items :+ e.item)
case e: OrderEvent.OrderConfirmed =>
order.copy(status = OrderStatus.Confirmed)
case e: OrderEvent.OrderShipped =>
order.copy(status = OrderStatus.Shipped)
}
// 从事件流重建聚合
def rebuild(orderId: OrderId, events: List[OrderEvent]): Option[Order] = {
if (events.isEmpty) None
else {
val initial = empty(orderId)
Some(events.foldLeft(initial)(applyEvent))
}
}
}
// 事件存储库
trait EventStore {
def append(events: List[OrderEvent]): IO[Unit]
def getEvents(orderId: OrderId): IO[List[OrderEvent]]
def getAllEvents(since: Option[Instant] = None): IO[List[OrderEvent]]
}
仓储(Repository)模式在函数式DDD中抽象了聚合的持久化操作。
// TypeScript:函数式仓储接口
import { Either } from 'fp-ts/Either';
import { Option } from 'fp-ts/Option';
import { IO } from 'fp-ts/IO';
import { TaskEither } from 'fp-ts/TaskEither';
interface RepositoryError {
type: 'NOT_FOUND' | 'CONFLICT' | 'CONNECTION_ERROR' | 'UNKNOWN';
message: string;
}
interface OrderRepository {
// 查找(可能不存在)
findById(id: OrderId): TaskEither<RepositoryError, Option<Order>>;
// 查找(必须存在)
getById(id: OrderId): TaskEither<RepositoryError, Order>;
// 保存(幂等操作)
save(order: Order): TaskEither<RepositoryError, Order>;
// 保存(乐观锁检查)
saveWithVersion(order: Order, expectedVersion: number): TaskEither<RepositoryError, Order>;
// 删除
delete(id: OrderId): TaskEither<RepositoryError, void>;
// 查询
findByCustomerId(customerId: CustomerId): TaskEither<RepositoryError, Order[]>;
findByStatus(status: OrderStatus): TaskEither<RepositoryError, Order[]>;
}
// 实现示例
class PostgresOrderRepository implements OrderRepository {
constructor(private db: Knex) {}
findById(id: OrderId): TaskEither<RepositoryError, Option<Order>> {
return TaskEither.tryCatch(
async () => {
const row = await this.db('orders')
.where('id', id.value)
.first();
return row ? Option.some(this.toDomain(row)) : Option.none;
},
(error) => ({
type: 'CONNECTION_ERROR',
message: error instanceof Error ? error.message : 'Unknown error'
})
);
}
private toDomain(row: any): Order {
// 转换逻辑
return Order.reconstitute({
id: OrderId(row.id),
customerId: CustomerId(row.customer_id),
// ...
});
}
}
在函数式编程中,事务可以通过 Effect 系统或 Monad 转换器来管理:
// Scala:函数式事务管理
import cats.data.{EitherT, Kleisli}
import cats.effect.IO
type Transaction[A] = Kleisli[IO, Connection, A]
class TransactionalOrderRepository(
db: Database
) {
def save(order: Order): Transaction[Either[RepositoryError, Order]] = Kleisli { conn =>
IO {
try {
// 插入或更新订单
val orderSql = """
INSERT INTO orders (id, customer_id, status, created_at, version)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE SET
status = EXCLUDED.status,
version = EXCLUDED.version
"""
conn.prepareStatement(orderSql).use { stmt =>
stmt.setString(1, order.id.value)
stmt.setString(2, order.customerId.value)
stmt.setString(3, order.status.toString)
stmt.setTimestamp(4, Timestamp.from(order.createdAt))
stmt.setLong(5, order.version)
stmt.executeUpdate()
}
// 删除旧订单项
conn.prepareStatement("DELETE FROM order_items WHERE order_id = ?").use { stmt =>
stmt.setString(1, order.id.value)
stmt.executeUpdate()
}
// 插入新订单项
val itemSql = """
INSERT INTO order_items (order_id, product_id, product_name, unit_price, quantity)
VALUES (?, ?, ?, ?, ?)
"""
conn.prepareStatement(itemSql).use { stmt =>
order.items.foreach { item =>
stmt.setString(1, order.id.value)
stmt.setString(2, item.productId.value)
stmt.setString(3, item.productName)
stmt.setBigDecimal(4, item.unitPrice.amount)
stmt.setInt(5, item.quantity)
stmt.addBatch()
}
stmt.executeBatch()
}
Right(order)
} catch {
case e: SQLException => Left(RepositoryError.DatabaseError(e.getMessage))
}
}
}
}
// 事务执行器
class TransactionExecutor(db: Database) {
def run[A](transaction: Transaction[A]): IO[A] = {
db.getConnection.use { conn =>
IO(conn.setAutoCommit(false)) >>
transaction.run(conn).attempt.flatMap {
case Right(result) => IO(conn.commit()) >> IO.pure(result)
case Left(error) => IO(conn.rollback()) >> IO.raiseError(error)
}
}
}
}
六边形架构(Hexagonal Architecture)与函数式编程理念高度契合,都强调核心业务逻辑与外部依赖的分离。
// Kotlin:六边形架构实现
// ========== 领域层(纯函数核心)==========
// 领域端口(接口定义)
interface ForInventory {
fun checkAvailability(productId: ProductId, quantity: Int): Either<InventoryError, Unit>
fun reserve(productId: ProductId, quantity: Int, reservationId: ReservationId): Either<InventoryError, Unit>
fun release(reservationId: ReservationId): Either<InventoryError, Unit>
}
interface ForPayment {
fun processPayment(orderId: OrderId, amount: Money, method: PaymentMethod): Either<PaymentError, TransactionId>
}
// 领域服务(纯函数)
object OrderPlacementService {
fun placeOrder(
customerId: CustomerId,
items: List<OrderItem>,
shippingAddress: Address,
inventory: ForInventory,
payment: ForPayment
): Either<DomainError, Order> {
// 1. 验证库存
val inventoryCheck = items.traverse { item ->
inventory.checkAvailability(item.productId, item.quantity)
}.mapLeft { DomainError.InventoryError(it) }
// 2. 创建订单
val order = Order.create(customerId, items, shippingAddress)
// 3. 预留库存
val reservation = inventory.reserve(order.id, items)
// 组合结果
return inventoryCheck.map { order }
}
}
// ========== 应用层 ==========
class OrderApplicationService(
private val orderRepository: OrderRepository,
private val inventoryAdapter: ForInventory,
private val paymentAdapter: ForPayment,
private val eventPublisher: EventPublisher
) {
suspend fun execute(command: PlaceOrderCommand): Result<Order> {
return OrderPlacementService.placeOrder(
customerId = command.customerId,
items = command.items,
shippingAddress = command.shippingAddress,
inventory = inventoryAdapter,
payment = paymentAdapter
).fold(
{ error -> Result.failure(error.toException()) },
{ order ->
// 保存订单
orderRepository.save(order)
// 发布事件
eventPublisher.publish(OrderPlacedEvent(order))
Result.success(order)
}
)
}
}
// ========== 适配器层 ==========
class InventoryRestAdapter(
private val httpClient: HttpClient,
private val baseUrl: String
) : ForInventory {
override fun checkAvailability(productId: ProductId, quantity: Int): Either<InventoryError, Unit> {
return try {
val response = httpClient.get("$baseUrl/api/inventory/${productId.value}/availability") {
parameter("quantity", quantity)
}
if (response.status == HttpStatusCode.OK) {
Either.Right(Unit)
} else {
Either.Left(InventoryError.InsufficientStock(productId))
}
} catch (e: Exception) {
Either.Left(InventoryError.ServiceUnavailable)
}
}
override fun reserve(
productId: ProductId,
quantity: Int,
reservationId: ReservationId
): Either<InventoryError, Unit> {
// 实现...
}
override fun release(reservationId: ReservationId): Either<InventoryError, Unit> {
// 实现...
}
}
命令查询职责分离(CQRS)模式将读操作和写操作分离,与函数式编程的不可变性理念相得益彰。
// Scala:CQRS 命令端
sealed trait OrderCommand {
def orderId: OrderId
}
object OrderCommand {
case class PlaceOrder(
customerId: CustomerId,
items: List[OrderItem],
shippingAddress: Address
) extends OrderCommand {
def orderId: OrderId = OrderId.generate()
}
case class ConfirmOrder(
orderId: OrderId,
confirmedBy: UserId
) extends OrderCommand
case class ShipOrder(
orderId: OrderId,
trackingNumber: TrackingNumber,
carrier: Carrier
) extends OrderCommand
}
// 命令处理器
trait CommandHandler[C <: OrderCommand] {
def handle(command: C): IO[Either[CommandError, List[DomainEvent]]]
}
class PlaceOrderHandler(
inventoryService: InventoryService,
pricingService: PricingService,
orderRepository: OrderRepository
) extends CommandHandler[OrderCommand.PlaceOrder] {
override def handle(command: OrderCommand.PlaceOrder): IO[Either[CommandError, List[DomainEvent]]] = {
for {
// 验证库存
inventoryResult <- inventoryService.checkAvailability(command.items)
// 获取价格
pricedItems <- pricingService.calculatePrices(command.items)
// 创建订单
order = Order.create(
id = command.orderId,
customerId = command.customerId,
items = pricedItems,
shippingAddress = command.shippingAddress
)
// 保存
_ <- orderRepository.save(order)
} yield Right(List(
OrderPlacedEvent(
orderId = order.id,
customerId = order.customerId,
items = order.items,
totalAmount = order.totalAmount,
shippingAddress = order.shippingAddress
)
))
}.handleError { error =>
Left(CommandError.DomainError(error.getMessage))
}
}
// Scala:CQRS 查询端
// 查询模型(专门为读优化)
case class OrderView(
orderId: String,
customerName: String,
status: String,
totalAmount: BigDecimal,
currency: String,
itemCount: Int,
createdAt: Instant,
confirmedAt: Option[Instant],
shippedAt: Option[Instant]
)
case class OrderDetailView(
orderId: String,
customer: CustomerSummary,
status: String,
items: List[OrderItemView],
totalAmount: BigDecimal,
shippingAddress: AddressView,
timeline: List[OrderTimelineEvent]
)
// 查询接口
trait OrderQueries {
def findById(orderId: OrderId): IO[Option[OrderDetailView]]
def findByCustomer(customerId: CustomerId, limit: Int = 20): IO[List[OrderView]]
def findByStatus(status: OrderStatus, page: Int, size: Int): IO[Page[OrderView]]
def search(criteria: OrderSearchCriteria): IO[Page[OrderView]]
}
// 实现(使用专门的读模型数据库)
class OrderQueriesImpl(readDb: ReadDatabase) extends OrderQueries {
override def findById(orderId: OrderId): IO[Option[OrderDetailView]] = {
readDb.queryForObject[OrderDetailView]("""
SELECT o.*, c.name as customer_name, c.email as customer_email
FROM order_views o
JOIN customer_views c ON o.customer_id = c.id
WHERE o.order_id = ?
""", orderId.value)
}
override def findByCustomer(customerId: CustomerId, limit: Int): IO[List[OrderView]] = {
readDb.queryForList[OrderView]("""
SELECT * FROM order_views
WHERE customer_id = ?
ORDER BY created_at DESC
LIMIT ?
""", customerId.value, limit)
}
override def findByStatus(status: OrderStatus, page: Int, size: Int): IO[Page[OrderView]] = {
val offset = page * size
readDb.queryForPage[OrderView]("""
SELECT * FROM order_views
WHERE status = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
""", status.toString, size, offset)
}
}
Saga 模式用于管理分布式事务,在函数式编程中可以通过组合异步操作来实现。
// TypeScript:函数式 Saga 实现
import { TaskEither, tryCatch, chain, orElse, right, left } from 'fp-ts/TaskEither';
import { pipe } from 'fp-ts/function';
interface SagaContext {
orderId: OrderId;
compensationActions: Array<() => TaskEither<Error, void>>;
}
interface SagaError {
step: string;
originalError: Error;
compensationResult: 'SUCCESS' | 'PARTIAL' | 'FAILED';
}
class OrderPlacementSaga {
constructor(
private inventoryService: InventoryService,
private paymentService: PaymentService,
private shippingService: ShippingService,
private notificationService: NotificationService
) {}
execute(order: Order): TaskEither<SagaError, Order> {
const ctx: SagaContext = {
orderId: order.id,
compensationActions: []
};
return pipe(
// Step 1: 预留库存
this.reserveInventory(order, ctx),
// Step 2: 处理支付
chain(() => this.processPayment(order, ctx)),
// Step 3: 创建发货单
chain(() => this.createShipment(order, ctx)),
// Step 4: 发送通知
chain(() => this.sendNotification(order)),
// 成功返回订单
map(() => order),
// 失败时执行补偿
orElse((error) => this.compensate(ctx, error))
);
}
private reserveInventory(
order: Order,
ctx: SagaContext
): TaskEither<Error, void> {
return pipe(
tryCatch(
() => this.inventoryService.reserve(order.id, order.items),
(error) => error as Error
),
chain(() => {
// 记录补偿动作
ctx.compensationActions.push(() =>
this.inventoryService.releaseReservation(order.id)
);
return right(undefined);
})
);
}
private processPayment(
order: Order,
ctx: SagaContext
): TaskEither<Error, void> {
return pipe(
tryCatch(
() => this.paymentService.charge(order.customerId, order.totalAmount),
(error) => error as Error
),
chain((transactionId) => {
ctx.compensationActions.push(() =>
this.paymentService.refund(transactionId)
);
return right(undefined);
})
);
}
private createShipment(
order: Order,
ctx: SagaContext
): TaskEither<Error, void> {
return pipe(
tryCatch(
() => this.shippingService.createShipment(order.id, order.shippingAddress),
(error) => error as Error
),
chain((shipmentId) => {
ctx.compensationActions.push(() =>
this.shippingService.cancelShipment(shipmentId)
);
return right(undefined);
})
);
}
private sendNotification(order: Order): TaskEither<Error, void> {
return tryCatch(
() => this.notificationService.sendOrderConfirmation(order.customerId, order.id),
(error) => error as Error
);
}
private compensate(
ctx: SagaContext,
originalError: Error
): TaskEither<SagaError, never> {
// 逆序执行补偿操作
const reversedActions = [...ctx.compensationActions].reverse();
return pipe(
reversedActions.reduce(
(acc, action) => pipe(
acc,
chain(() => action())
),
right<Error, void>(undefined) as TaskEither<Error, void>
),
map(() => ({
step: 'COMPENSATION',
originalError,
compensationResult: 'SUCCESS' as const
})),
orElse((compError) => right({
step: 'COMPENSATION',
originalError,
compensationResult: 'FAILED' as const
})),
chain((result) => left(result))
);
}
}
在函数式DDD中,值对象应该是默认选择,只有在需要唯一标识时才使用实体。
// 好的实践:丰富的值对象
case class Money(amount: BigDecimal, currency: Currency) {
def +(other: Money): Money = {
require(currency == other.currency, "Currency mismatch")
copy(amount = amount + other.amount)
}
def *(factor: BigDecimal): Money =
copy(amount = amount * factor)
def negate: Money = copy(amount = -amount)
}
case class Email(value: String)
case class PhoneNumber(countryCode: String, number: String)
case class Address(street: String, city: String, zipCode: String, country: String)
利用类型系统使无效状态不可表示:
// Rust:使用类型系统防止无效状态
// 不好的做法:使用布尔标志
struct Order {
is_confirmed: bool,
is_shipped: bool,
is_cancelled: bool,
}
// 好的做法:使用枚举确保状态互斥
enum OrderStatus {
Pending,
Confirmed { at: DateTime<Utc> },
Shipped { at: DateTime<Utc>, tracking: TrackingNumber },
Delivered { at: DateTime<Utc> },
Cancelled { at: DateTime<Utc>, reason: String },
}
struct Order {
status: OrderStatus,
}
impl Order {
fn confirm(mut self) -> Result<Self, OrderError> {
match self.status {
OrderStatus::Pending => {
self.status = OrderStatus::Confirmed { at: Utc::now() };
Ok(self)
}
_ => Err(OrderError::InvalidStateTransition),
}
}
}
通过智能构造器确保对象始终处于有效状态:
// Kotlin:智能构造器
class Order private constructor(
val id: OrderId,
val customerId: CustomerId,
val items: List<OrderItem>,
val status: OrderStatus
) {
companion object {
fun create(
id: OrderId,
customerId: CustomerId,
items: List<OrderItem>
): Either<ValidationError, Order> {
if (items.isEmpty()) {
return ValidationError.EmptyOrder.left()
}
val invalidItems = items.filter { it.quantity <= 0 }
if (invalidItems.isNotEmpty()) {
return ValidationError.InvalidQuantity(invalidItems.map { it.productId }).left()
}
return Order(
id = id,
customerId = customerId,
items = items,
status = OrderStatus.Pending
).right()
}
// 用于从数据库重建对象
fun reconstitute(
id: OrderId,
customerId: CustomerId,
items: List<OrderItem>,
status: OrderStatus
): Order = Order(id, customerId, items, status)
}
}
避免将领域逻辑全部放在服务中,实体和值对象应该封装自己的行为:
// 不好的做法:贫血领域模型
case class Order(id: OrderId, items: List[OrderItem], status: OrderStatus)
object OrderService {
def calculateTotal(order: Order): Money =
order.items.map(_.subtotal).sum
def canConfirm(order: Order): Boolean =
order.status == OrderStatus.Pending
}
// 好的做法:充血领域模型
case class Order(
id: OrderId,
items: List[OrderItem],
status: OrderStatus
) {
def totalAmount: Money =
items.map(_.subtotal).reduce(_ + _)
def confirm(at: Instant): Either[OrderError, Order] = status match {
case OrderStatus.Pending =>
Right(this.copy(status = OrderStatus.Confirmed))
case _ =>
Left(OrderError.InvalidTransition(status, OrderStatus.Confirmed))
}
}
虽然 Monad 是强大的抽象,但过度使用会导致代码难以阅读。在简单场景下,使用普通函数即可:
// 过度使用 Monad(不推荐)
def processOrder(orderId: OrderId): IO[Either[DomainError, Order]] = {
for {
order <- EitherT(orderRepository.findById(orderId))
.toRight(DomainError.OrderNotFound(orderId))
validated <- EitherT.fromEither[IO](validateOrder(order))
processed <- EitherT(processOrderLogic(validated))
} yield processed
}.value
// 更简洁的方式(推荐)
def processOrder(orderId: OrderId): IO[Either[DomainError, Order]] = {
orderRepository.findById(orderId).map {
case None => Left(DomainError.OrderNotFound(orderId))
case Some(order) =>
for {
validated <- validateOrder(order)
processed <- processOrderLogic(validated)
} yield processed
}
}
不可变数据结构在某些场景下可能带来性能开销,需要注意:
// 性能问题:大量小对象的创建
fun processLargeList(items: List<Item>): List<Result> {
return items.map { item ->
// 每个 map 操作都创建新列表
item.validate()
}.map { validated ->
// 又一个新列表
validated.transform()
}.map { transformed ->
// 再一个新列表
transformed.save()
}
}
// 优化:使用序列(惰性求值)
fun processLargeListOptimized(items: List<Item>): List<Result> {
return items.asSequence()
.map { it.validate() }
.map { it.transform() }
.map { it.save() }
.toList() // 只创建一次最终列表
}
函数式DDD代码具有天然的测试友好性:
// Scala:纯函数的单元测试
class OrderSpec extends AnyFlatSpec with Matchers {
"Order.totalAmount" should "calculate the sum of all items" in {
val order = Order.create(
id = OrderId("order-1"),
customerId = CustomerId("customer-1"),
items = List(
OrderItem(ProductId("p1"), "Product A", Money(100, CNY), Quantity(2)),
OrderItem(ProductId("p2"), "Product B", Money(50, CNY), Quantity(3))
)
)
order.totalAmount shouldBe Money(350, CNY)
}
"Order.confirm" should "transition from Pending to Confirmed" in {
val order = createPendingOrder()
val confirmed = order.confirm(Instant.now())
confirmed.isRight shouldBe true
confirmed.toOption.get.status shouldBe OrderStatus.Confirmed
}
it should "fail when order is not in Pending status" in {
val order = createConfirmedOrder()
val result = order.confirm(Instant.now())
result.isLeft shouldBe true
result.swap.toOption.get shouldBe a[OrderError.InvalidStatusTransition]
}
}
// 属性测试(使用 ScalaCheck)
class OrderProperties extends AnyPropSpec with ScalaCheckDrivenPropertyChecks {
property("order total is always sum of item subtotals") {
forAll { (items: List[OrderItem]) =>
whenever(items.nonEmpty && items.forall(_.quantity.value > 0)) {
val order = Order.create(OrderId.generate(), CustomerId.generate(), items)
val expectedTotal = items.map(_.subtotal).reduce(_ + _)
order.totalAmount shouldBe expectedTotal
}
}
}
}
Scala 是函数式DDD的理想语言,结合了面向对象和函数式编程的特性:
// Scala:完整的订单聚合实现
import java.time.Instant
import java.util.UUID
import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.syntax.all._
// 值对象
final case class OrderId(value: UUID) extends AnyVal
final case class CustomerId(value: UUID) extends AnyVal
final case class ProductId(value: UUID) extends AnyVal
final case class Money(amount: BigDecimal, currency: Currency)
object Money {
def zero(currency: Currency): Money = Money(BigDecimal(0), currency)
implicit val moneySemigroup: cats.Semigroup[Money] = (x: Money, y: Money) => {
require(x.currency == y.currency, "Currency mismatch")
Money(x.amount + y.amount, x.currency)
}
}
sealed trait Currency
object Currency {
case object CNY extends Currency
case object USD extends Currency
case object EUR extends Currency
}
// 领域错误
sealed trait OrderError
object OrderError {
case object EmptyOrder extends OrderError
case class InvalidQuantity(productId: ProductId) extends OrderError
case class InvalidStatusTransition(from: OrderStatus, to: OrderStatus) extends OrderError
case object OrderAlreadyCancelled extends OrderError
}
// 订单状态
sealed trait OrderStatus
object OrderStatus {
case object Pending extends OrderStatus
case object Confirmed extends OrderStatus
case object Shipped extends OrderStatus
case object Delivered extends OrderStatus
case object Cancelled extends OrderStatus
}
// 订单项
case class OrderItem(
productId: ProductId,
productName: String,
unitPrice: Money,
quantity: Quantity
) {
def subtotal: Money = unitPrice.copy(amount = unitPrice.amount * quantity.value)
}
case class Quantity(value: Int) {
require(value > 0, "Quantity must be positive")
require(value <= 10000, "Quantity cannot exceed 10000")
}
// 订单聚合
case class Order private (
id: OrderId,
customerId: CustomerId,
items: NonEmptyList[OrderItem],
status: OrderStatus,
createdAt: Instant,
confirmedAt: Option[Instant] = None,
shippedAt: Option[Instant] = None,
cancelledAt: Option[Instant] = None,
cancellationReason: Option[String] = None
) {
def totalAmount: Money =
items.map(_.subtotal).reduce(_ |+| _)
def confirm(at: Instant): Either[OrderError, Order] = status match {
case OrderStatus.Pending =>
Right(this.copy(status = OrderStatus.Confirmed, confirmedAt = Some(at)))
case _ =>
Left(OrderError.InvalidStatusTransition(status, OrderStatus.Confirmed))
}
def ship(at: Instant, trackingNumber: String): Either[OrderError, Order] = status match {
case OrderStatus.Confirmed =>
Right(this.copy(status = OrderStatus.Shipped, shippedAt = Some(at)))
case _ =>
Left(OrderError.InvalidStatusTransition(status, OrderStatus.Shipped))
}
def cancel(at: Instant, reason: String): Either[OrderError, Order] = status match {
case OrderStatus.Cancelled => Left(OrderError.OrderAlreadyCancelled)
case OrderStatus.Delivered =>
Left(OrderError.InvalidStatusTransition(status, OrderStatus.Cancelled))
case _ =>
Right(this.copy(
status = OrderStatus.Cancelled,
cancelledAt = Some(at),
cancellationReason = Some(reason)
))
}
}
object Order {
def create(
id: OrderId,
customerId: CustomerId,
items: List[OrderItem]
): ValidatedNel[OrderError, Order] = {
val validItems = items.traverse { item =>
if (item.quantity.value <= 0)
Validated.invalidNel(OrderError.InvalidQuantity(item.productId))
else
Validated.validNel(item)
}
validItems.andThen { itemList =>
NonEmptyList.fromList(itemList) match {
case Some(nel) =>
Validated.validNel(Order(
id = id,
customerId = customerId,
items = nel,
status = OrderStatus.Pending,
createdAt = Instant.now()
))
case None =>
Validated.invalidNel(OrderError.EmptyOrder)
}
}
}
}
Kotlin 提供了实用的函数式编程支持,适合渐进式采用函数式DDD:
// Kotlin:使用 Arrow 库的函数式DDD
import arrow.core.*
import arrow.core.raise.*
import java.math.BigDecimal
import java.time.Instant
import java.util.UUID
// 值对象
@JvmInline
value class OrderId(val value: UUID) {
companion object {
fun generate(): OrderId = OrderId(UUID.randomUUID())
fun fromString(id: String): OrderId = OrderId(UUID.fromString(id))
}
}
@JvmInline
value class CustomerId(val value: UUID)
@JvmInline
value class ProductId(val value: UUID)
// 使用 Arrow 的 Raise DSL 进行验证
data class Money(val amount: BigDecimal, val currency: Currency) {
init {
require(amount >= BigDecimal.ZERO) { "Amount cannot be negative" }
}
operator fun plus(other: Money): Money {
require(currency == other.currency) { "Currency mismatch" }
return Money(amount + other.amount, currency)
}
operator fun times(factor: Int): Money =
Money(amount * factor.toBigDecimal(), currency)
companion object {
fun zero(currency: Currency) = Money(BigDecimal.ZERO, currency)
}
}
enum class Currency { CNY, USD, EUR }
// 领域错误
sealed interface OrderError {
data object EmptyOrder : OrderError
data class InvalidQuantity(val productId: ProductId) : OrderError
data class InvalidStatusTransition(val from: OrderStatus, val to: OrderStatus) : OrderError
data object OrderAlreadyCancelled : OrderError
}
// 使用 Raise DSL 的验证函数
context(Raise<OrderError>)
fun validateOrderItems(items: List<OrderItem>): NonEmptyList<OrderItem> {
ensure(items.isNotEmpty()) { OrderError.EmptyOrder }
items.forEach { item ->
ensure(item.quantity > 0) { OrderError.InvalidQuantity(item.productId) }
}
return NonEmptyList(items.first(), items.drop(1))
}
// 订单状态
sealed interface OrderStatus {
data object Pending : OrderStatus
data object Confirmed : OrderStatus
data object Shipped : OrderStatus
data object Delivered : OrderStatus
data object Cancelled : OrderStatus
}
// 订单项
data class OrderItem(
val productId: ProductId,
val productName: String,
val unitPrice: Money,
val quantity: Int
) {
val subtotal: Money get() = unitPrice * quantity
}
// 订单聚合
data class Order private constructor(
val id: OrderId,
val customerId: CustomerId,
val items: NonEmptyList<OrderItem>,
val status: OrderStatus,
val createdAt: Instant,
val confirmedAt: Instant? = null,
val shippedAt: Instant? = null
) {
val totalAmount: Money
get() = items.map { it.subtotal }.reduce { a, b -> a + b }
fun confirm(at: Instant): Either<OrderError, Order> = either {
ensure(status == OrderStatus.Pending) {
OrderError.InvalidStatusTransition(status, OrderStatus.Confirmed)
}
copy(status = OrderStatus.Confirmed, confirmedAt = at)
}
fun ship(at: Instant, trackingNumber: String): Either<OrderError, Order> = either {
ensure(status == OrderStatus.Confirmed) {
OrderError.InvalidStatusTransition(status, OrderStatus.Shipped)
}
copy(status = OrderStatus.Shipped, shippedAt = at)
}
companion object {
fun create(
id: OrderId,
customerId: CustomerId,
items: List<OrderItem>
): Either<OrderError, Order> = either {
val validItems = validateOrderItems(items)
Order(
id = id,
customerId = customerId,
items = validItems,
status = OrderStatus.Pending,
createdAt = Instant.now()
)
}
}
}
// 使用示例
fun main() {
val orderResult = Order.create(
id = OrderId.generate(),
customerId = CustomerId(UUID.randomUUID()),
items = listOf(
OrderItem(
productId = ProductId(UUID.randomUUID()),
productName = "Product A",
unitPrice = Money(BigDecimal("100.00"), Currency.CNY),
quantity = 2
)
)
)
when (orderResult) {
is Either.Right -> {
val order = orderResult.value
println("Order created: ${order.id.value}")
println("Total: ${order.totalAmount}")
val confirmed = order.confirm(Instant.now())
when (confirmed) {
is Either.Right -> println("Order confirmed!")
is Either.Left -> println("Failed to confirm: ${confirmed.value}")
}
}
is Either.Left -> println("Failed to create order: ${orderResult.value}")
}
}
TypeScript 结合 fp-ts 等库可以实现函数式DDD:
// TypeScript:使用 fp-ts 的函数式DDD
import { Either, left, right, chain, map, getOrElse } from 'fp-ts/Either';
import { pipe } from 'fp-ts/function';
import { NonEmptyArray } from 'fp-ts/NonEmptyArray';
import { v4 as uuidv4 } from 'uuid';
// 品牌类型(模拟 newtype)
type OrderId = string & { readonly brand: unique symbol };
type CustomerId = string & { readonly brand: unique symbol };
type ProductId = string & { readonly brand: unique symbol };
const OrderId = {
generate: (): OrderId => uuidv4() as OrderId,
fromString: (id: string): OrderId => id as OrderId
};
// 值对象
interface Money {
readonly amount: number;
readonly currency: Currency;
}
type Currency = 'CNY' | 'USD' | 'EUR';
const Money = {
create: (amount: number, currency: Currency): Either<string, Money> =>
amount >= 0
? right({ amount, currency })
: left('Amount cannot be negative'),
zero: (currency: Currency): Money => ({ amount: 0, currency }),
add: (a: Money, b: Money): Either<string, Money> =>
a.currency === b.currency
? right({ amount: a.amount + b.amount, currency: a.currency })
: left('Currency mismatch'),
multiply: (money: Money, factor: number): Money => ({
amount: money.amount * factor,
currency: money.currency
})
};
// 领域错误
type OrderError =
| { type: 'EMPTY_ORDER' }
| { type: 'INVALID_QUANTITY'; productId: ProductId }
| { type: 'INVALID_STATUS_TRANSITION'; from: OrderStatus; to: OrderStatus }
| { type: 'ORDER_ALREADY_CANCELLED' };
// 订单状态
type OrderStatus =
| { type: 'PENDING' }
| { type: 'CONFIRMED'; at: Date }
| { type: 'SHIPPED'; at: Date; trackingNumber: string }
| { type: 'DELIVERED'; at: Date }
| { type: 'CANCELLED'; at: Date; reason: string };
const OrderStatus = {
Pending: (): OrderStatus => ({ type: 'PENDING' }),
Confirmed: (at: Date): OrderStatus => ({ type: 'CONFIRMED', at }),
Shipped: (at: Date, trackingNumber: string): OrderStatus =>
({ type: 'SHIPPED', at, trackingNumber }),
};
// 订单项
interface OrderItem {
readonly productId: ProductId;
readonly productName: string;
readonly unitPrice: Money;
readonly quantity: number;
}
const OrderItem = {
create: (
productId: ProductId,
productName: string,
unitPrice: Money,
quantity: number
): Either<OrderError, OrderItem> =>
quantity > 0
? right({ productId, productName, unitPrice, quantity })
: left({ type: 'INVALID_QUANTITY', productId }),
subtotal: (item: OrderItem): Money =>
Money.multiply(item.unitPrice, item.quantity)
};
// 订单聚合
interface Order {
readonly id: OrderId;
readonly customerId: CustomerId;
readonly items: NonEmptyArray<OrderItem>;
readonly status: OrderStatus;
readonly createdAt: Date;
}
const Order = {
create: (
id: OrderId,
customerId: CustomerId,
items: OrderItem[]
): Either<OrderError, Order> => {
if (items.length === 0) {
return left({ type: 'EMPTY_ORDER' });
}
const invalidItem = items.find(item => item.quantity <= 0);
if (invalidItem) {
return left({ type: 'INVALID_QUANTITY', productId: invalidItem.productId });
}
return right({
id,
customerId,
items: items as NonEmptyArray<OrderItem>,
status: OrderStatus.Pending(),
createdAt: new Date()
});
},
totalAmount: (order: Order): Money =>
order.items.reduce((total, item) => {
const subtotal = OrderItem.subtotal(item);
return Money.add(total, subtotal).pipe(
getOrElse(() => total) // 理论上不会发生,因为货币相同
);
}, Money.zero(order.items[0].unitPrice.currency)),
confirm: (order: Order, at: Date): Either<OrderError, Order> => {
if (order.status.type !== 'PENDING') {
return left({
type: 'INVALID_STATUS_TRANSITION',
from: order.status,
to: OrderStatus.Confirmed(at)
});
}
return right({ ...order, status: OrderStatus.Confirmed(at) });
},
ship: (order: Order, at: Date, trackingNumber: string): Either<OrderError, Order> => {
if (order.status.type !== 'CONFIRMED') {
return left({
type: 'INVALID_STATUS_TRANSITION',
from: order.status,
to: OrderStatus.Shipped(at, trackingNumber)
});
}
return right({ ...order, status: OrderStatus.Shipped(at, trackingNumber) });
}
};
// 使用示例
const orderWorkflow = pipe(
Order.create(
OrderId.generate(),
OrderId.generate() as unknown as CustomerId,
[
{
productId: OrderId.generate() as unknown as ProductId,
productName: 'Product A',
unitPrice: { amount: 100, currency: 'CNY' },
quantity: 2
}
]
),
chain(order => Order.confirm(order, new Date())),
chain(order => Order.ship(order, new Date(), 'TRACK123'))
);
// 处理结果
orderWorkflow.match(
error => console.error('Error:', error),
order => console.log('Order shipped:', order.id, 'Total:', Order.totalAmount(order))
);
函数式领域驱动设计将函数式编程的数学严谨性与领域驱动设计的业务聚焦相结合,为构建复杂软件系统提供了强大的方法论。
不可变性作为默认选择:优先使用不可变数据结构,使代码更易于理解、测试和并发执行。
类型驱动设计:利用类型系统使无效状态不可表示,在编译期捕获错误。
纯函数核心业务:将业务逻辑封装在纯函数中,副作用隔离在系统边界。
显式建模状态:使用和类型(密封类/代数数据类型)精确建模业务状态,避免布尔标志和空值。
函数组合构建流程:使用函数组合构建验证管道、业务工作流和数据转换流程。
Effect 系统管理副作用:使用 Effect 系统或 Monad 转换器显式追踪和管理副作用。
函数式DDD特别适合以下场景:
渐进式采用:不必一次性重写整个系统,可以从核心领域模型开始逐步引入函数式概念。
团队培训:确保团队成员理解函数式编程的基本概念和最佳实践。
选择合适的语言:Scala、Kotlin、F#、Rust 等语言对函数式DDD有良好支持。
关注业务价值:始终将业务需求放在首位,技术选择服务于业务目标。
函数式领域驱动设计不是银弹,但它为解决复杂软件问题提供了强大的工具和思维模式。通过实践和持续学习,团队可以逐步掌握这种方法,构建出更加健壮、可维护的软件系统。