DDD中ValueObject的合并:用类内静态方法还是工具类?(C#)
我最近看到一段在应用层调用的、用于合并两个值对象的代码:
public class SomeValueObject { public readonly string A; public readonly string B; public SomeValueObject(string a, string b) { A = a; B = b; } public static SomeValueObject Merge(SomeValueObject preferred, SomeValueObject fallback) { return new SomeValueObject( preferred.A ?? fallback.A, preferred.B ?? fallback.B); } }
针对这段代码以及后续扩展场景,我有以下疑问:
- 像示例这样在ValueObject类内用静态方法实现合并是否为最佳实践?还是应使用工具类?
- 若后续还需添加其他静态方法(例如从多个其他ValueObject的属性创建当前ValueObject),我倾向于用工具类实现这类功能,但在返回值的ValueObject类内实现静态方法是否符合最佳实践?示例代码如下:
public class SomeValueObject { public readonly string A; public readonly string B; public SomeValueObject(string a, string b) { A = a; B = b; } public static SomeValueObject Merge(SomeValueObject preferred, SomeValueObject fallback) { return new SomeValueObject( preferred.A ?? fallback.A, preferred.B ?? fallback.B); } public static SomeValueObject FromOtherObjects(SomeOtherValueObject someOtherValueObject, SomeSecondOtherValueObject someSecondOtherValueObject) { return new SomeValueObject(someOtherValueObject.Foo, someSecondOtherValueObject.bar); } }
- 针对问题2,在当前类中依赖其他类(但这些类并非当前类的属性),若其他类也存在依赖当前类的方法,会导致类之间出现循环依赖。
我个人更倾向于使用工具类,但不确定是否属于过度设计?
解答
1. 值对象内静态方法合并 vs 工具类:哪种更优?
这其实取决于单一职责原则和值对象的封装边界。
值对象的核心职责是封装一组相关值,并保证自身的不变性和业务规则。如果合并逻辑是该值对象本身的核心业务行为(比如“合并两个用户偏好配置值对象”是配置值对象的固有能力),那么在类内实现静态Merge方法完全合理——它符合“高内聚”的设计原则,让开发者看到SomeValueObject时就能知道它支持合并操作,不用去其他地方找工具类。
但如果合并逻辑是跨多个值对象的通用操作,或者和当前值对象的核心业务无关,那工具类会更合适。不过对于你给出的示例,合并是针对同类型值对象的,放在类内是更自然的选择,很多成熟的DDD实践里也会这么做(比如Money值对象的Add方法)。
2. 新增跨类型转换静态方法:放在值对象内是否符合最佳实践?
这里的关键是依赖方向和职责边界。
如果FromOtherObjects是创建当前值对象的标准方式,且这种转换逻辑是当前值对象的“构造扩展”,放在类内作为静态工厂方法是没问题的——这是DDD中值对象常见的设计方式,用来替代过多的重载构造函数,让对象创建语义更清晰(比如UserProfile.FromUserAndSettings(user, settings))。
但如果这种转换逻辑是业务场景特定的(比如只在某个订单处理流程里需要从这两个其他值对象创建当前值对象),那放在工具类或者对应的业务服务里会更合适,避免值对象被无关的业务逻辑污染。
3. 循环依赖问题与工具类是否过度设计?
你担心的循环依赖确实是个实际问题。如果SomeValueObject依赖SomeOtherValueObject,而后者又有方法依赖SomeValueObject,那在类内写静态方法会直接导致程序集循环依赖,这是需要避免的。
这种情况下,工具类就成了更稳妥的选择——它作为中间层,打破循环依赖。至于是否是过度设计?要看场景:
- 如果你的项目已经出现了潜在的循环依赖风险,或者这类转换/合并操作会越来越多、涉及越来越多的其他类型,那工具类是合理的,它能保持值对象的纯净,也便于统一维护这些操作。
- 如果只是少数几个简单的转换,且没有循环依赖的可能,那放在值对象内更简洁,没必要强行用工具类。
总结建议
- 同类型值对象的合并/操作:优先放在值对象内,符合高内聚原则。
- 跨类型的对象创建/转换:如果是通用的、值对象本身的构造逻辑,用静态工厂方法;如果是业务场景特定的,或者会引发循环依赖,用工具类或业务服务。
- 工具类不是过度设计,只要它解决的是实际问题(比如循环依赖、分散的操作维护),就是合理的设计选择。
内容的提问来源于stack exchange,提问作者b4n4n4

