设计应用程序层并非易事。在一天结束时,在每个项目中,我们编写(大量)所谓的业务方法(有时称为服务方法,尽管它与服务无关)在公共API中执行某些业务逻辑并且通常执行一些数据库/存储操作(通过存储层)。但是设计/组织/命名你的应用程序方法听起来并不简单;人们经常对项目规则应该如何设计应用程序类和方法。我正在寻找用于设计应用程序层的实用的一套规则,所以这里有一些问题:
在类中对方法进行分组:按返回类型或?
例如,更新用户的方法属于类UserService
。或者通过某些条件查找用户的方法属于同一个类。但是当某些方法适用于多种类型时会发生什么,例如registerUserForCompetition()
?这应该在UserService
还是CompetitionService
?方法findUserCompetition()
的问题相同 - 你会把它放在哪里?
我经常看到一条规则说这取决于方法返回类型。在我们的示例中,如果方法返回User
(或用户集合等),则它应属于UserService
。
方法参数:简单类型(基元,字符串...)或实体(例如User
,Competition
,Registration
......)?
这个问题经常被问到,人们通常(统计学上)选择第一种方法,使用简单类型作为服务参数。但是当一个这样的方法在同一个资源上调用另一个方法时该怎么办?我举个例子:registerUser(userId, competitionId)
可以在内部调用checkCompetition(competitionId)
。两种方法都从存储中获取Competition
。显然,这已经完成了两次,因为我们已经拥有Competition
对象,所以我们可以将它用于checkCompetition
。那么我们应该添加重载方法,还是我们应该忽略这个并且只是真的在缓存机制上来防止双重提取?
另一方面,将完整类型作为参数不能成为通用规则,因为很多时候不需要完整信息,并且没有理由在需要它的同时获取完整对象,你可能已经拥有它了。
命名方法:详细方法名称应该如何?
特别是对于取景器方法。我们应该有:例如:
findCompetition(userId, year)
或findCompetitionWithVenue(userId, year)
(因为每个Competition
对象都链接了Venue
);或findCompetitionForUserAndYear(userId, year)
或findCompetitionByUserIdAndYear(userId, year)
或只是findUserCompetitionForYear(userId, year)
?当不同标准的数量上升时,很容易弄乱查找器方法名称,我经常看到这样的规则:find<return-type>By<names-of-parameters>
或类似。
您是否有应用程序类的命名约定?
您的班级名称是否以Service
结尾?您是否将经理与服务分开,其中经理处理实体和服务只接受简单类型?您是否将所有业务类分组到同一个包中,或者根据功能将它们与其他相关类(模型,控制器......)组合在一起?
请分享您的经验,专注于务实 - 对我而言,这意味着新开发人员可以轻松地找到在哪里找到业务方法以及如何编写新方法,这样每个人都可以提高工作效率。此外,如果您已制定规则,请分享您的团队如何管理您的规则。
答案 0 :(得分:1)
按功能分组服务。在同一个包中也应该是控制器,模型的一部分等等,然后有更好的方法然后分层架构。试试DDD。它完美地隔离了功能,使其更容易找到和更改
通常,从客户端隐藏实体(模型)并通过DTO和ValueObjects与控制器通信是很好的。以防万一您希望将来更改您的模型
它们必须对您的域有意义且一致。如果你的团队说他们不清楚,不要害怕重构。我个人会跟findCompetition(user, year)
一起使用非基本类型,因为你可以使用方法重载但是在调用中你必须使用常量或读取启动器如findCompetition(forUserId(userId), forYear(year))
来明确地告诉读者你正在调用哪个版本。
什么是经理?这不代表任何意思。类似于助手,处理程序等。服务包含将DTO转换为实体的逻辑,并且逻辑不能被推送到域/实体,因为逻辑在许多实体上运行。如前所述,我按功能对组件(不仅是服务)进行分组,因此我添加了“服务”。后缀因为它可以帮助我快速找到特定包中的逻辑(包含模型,控制器,服务,存储库等)