Clean Architecture中如何设置实体间关系?博客场景架构设计疑问
问题1:Blog Domain同时设User和Post实体,Auth Domain如何处理?
不要在Blog Domain里定义完整的User实体——Blog Domain只需要和自身业务相关的User属性与行为,比如authorId、getDisplayName()这类和博客创作相关的内容,不需要包含密码、权限验证这类Auth Domain专属的逻辑。Auth Domain里的User实体专注于身份认证相关的属性(如密码哈希、登录凭证)和行为(如verifyPassword())。
两个Domain的User实体通过**唯一标识(比如userId)**关联,而不是共享同一个类。这样既符合Clean Architecture中“每个Domain只关注自身核心业务”的原则,也避免了Domain间的耦合。
问题2:仅Blog Domain设Post实体,如何关联User?
Post实体只需要持有User的唯一标识(如authorId),不需要直接依赖Auth Domain的User实体。当需要获取用户的展示信息(比如作者名称)时,通过Blog Domain的Repository接口来获取——这个接口由外层的基础设施层实现,基础设施层可以调用Auth Domain的服务来获取所需的用户数据。
举个简单的代码示例:
// Blog Domain的Post实体 public class Post { private String id; private String title; private String content; private String authorId; // 仅持有用户ID // 业务逻辑:验证作者是否有权限发布 public boolean canPublish(AuthorPermissionChecker checker) { return checker.hasPermission(authorId); } } // Blog Domain的Repository接口 public interface PostAuthorInfoRepository { AuthorInfo getAuthorInfoById(String authorId); } // 基础设施层实现接口,调用Auth服务 public class AuthBasedAuthorInfoRepository implements PostAuthorInfoRepository { private final AuthUserService authUserService; public AuthorInfo getAuthorInfoById(String authorId) { AuthUser user = authUserService.getUserById(authorId); return new AuthorInfo(user.getId(), user.getDisplayName()); } }
这种方式既保持了Blog Domain的独立性,又能通过依赖反转原则实现与User的数据关联。
问题3:两个Domain分别设User实体,如何避免代码重复?
首先要明确:两个Domain的User实体并不是“重复代码”——它们服务于不同的业务场景,属性和行为本来就应该有差异。但如果确实有一些通用的属性(比如userId、displayName),可以提取到共享的基础Domain包(比如Shared.Domain)中,作为一个值对象(Value Object)或者基础实体类,让两个Domain的User实体继承或组合这个基础类。
比如:
// Shared Domain的基础值对象 public class UserIdentity { private final String id; private final String displayName; // 构造器、getter等 } // Auth Domain的User实体 public class AuthUser { private UserIdentity identity; private String passwordHash; private LocalDateTime lastLoginTime; // 身份认证相关行为:verifyPassword()等 } // Blog Domain的Author实体 public class Author { private UserIdentity identity; private int postCount; // 博客相关行为:increasePostCount()等 }
这种方式既保留了各Domain的独立性,又复用了通用的属性逻辑,同时避免了Domain间的直接耦合。另外,不要为了复用而强行合并实体——每个Domain的实体必须优先满足自身的业务需求,复用是次要的。
内容的提问来源于stack exchange,提问作者Eternal Learner

