Java Swing应用程序中的服务层

9

我在思考是否真的需要一个服务层。

我正在为一个桌面swing应用程序使用spring + hibernate,目前我有gui / swing层 ->服务层 ->dao层。我只使用spring来支持@Transactional和IOC注入。

最佳实践指出,我必须编写一个服务来使用我的dao,并将所有事务管理放在服务中。

但是我意识到,服务层很多时候只是重复dao方法,例如:

// a DAO example
@Repository
public class CustomerHibernateDAO extends BaseHibernateDAO implements CustomerDAO {

 public List<Customer> findAllCustomerILikeName(String name){
  return getSession()
   .createCriteria(Customer.class)
   .add(Restriction.ilike("name", name))
   .list();
 }
}

// Customer service to use this dao...
@Service
@Transactional
public class CustomerService {

 @Autowired
 CustomerDAO customerDAO;

 // Why i can't call DAO instead the service?
 public List<Customer> getAllCustomersByName(String name){
      return customerDAO.findAllCustomerILikeName(name);
 }

}

这是服务层的一个典型用法……Hibernate是数据库无关的,Spring是技术无关的:那么,我真的需要它吗?
为什么不使用一个唯一的Service类来管理所有DAO?我认为这可能是一个很好的妥协,或者说,这是一个不好的做法吗?
我知道在DAO上放置@Transactional是个不好的方式,但现在我必须只写服务才能将其放置@Transactional……
编辑
我的应用程序是一个管理软件,管理用户注册、产品、订单和其他类似的事物。实际上,它包含许多读取实体->编辑->保存实体或创建->编辑->保存操作,并且由于Hibernate的@manyto...集合和cascade.save_update,这些操作大部分时间都由一个dao进行管理, 它允许在同一个持久化操作中保存两个或多个实体。
因此,例如,在我的项目JFrame中,我可以插入、编辑或创建一个商品(一个要出售的产品),其中包括:
public ItemFrame(){
 // the constructor
 itemService=springAppContext.getBeans(ItemService.class);
}

public boolean validateForm(){
 // test if the gui is correctly filled by user
}

public boolean save(){
 // create an Item entity taking value from swing gui(JTextField etc)
 Item item=new Item();
 item.setName(nameTextField.getText());
 item.setEtc...
 // ItemService ' save method is a wrap around itemDao.save(item)...
 itemService.save(item);
}

private void saveItemActionPerformed(ActionEvent evt){
 // When i press SAVE button
 if(validateForm()){
  save();
 }
}

我在大多数情况下遇到了这个问题,所以我认为我陷入了贫血领域反模式...

谢谢。


根据我的看法,你正在正确地进行操作。每个实体应该有相应的服务。这将保持标准模式,如果数据库发生更改,你只需要重新构建DAO层。另外加一分。 - jmj
如果数据库发生改变而我没有使用服务,会有什么问题吗?我只需重建我的DAO,一切都会好起来...还是不会? - blow
我有点跑题了,但是在ItemFrame构造函数中自己从容器中获取bean的原因是什么?我看不出这种方法比让容器注入bean有任何好处。 - brabec
ItemFrame是通过new ItemFrame()实例化的,它不是Spring组件,因此@Autowired在这里不起作用。我对Spring还很陌生,但这就是我所知道的...希望是正确的。 - blow
更好的方法是使用@Configurable注释...这样你可以自动装配到任何东西(甚至是通过Hibernate创建的实体(Hibernate可能使用clazz.getinstance))...唯一不起作用的地方是servlet,原因不明 :-)....可配置的唯一缺点是,您必须使用代码仪器化和一些配置行,但它非常容易(在appContext.xml中大约有3行xml,如果您正在使用tomcat,则在其catalina-opts中有一个参数(javaagent)(spring-agent.jar路径)。 - malejpavouk
4个回答

4
如果您的服务层重复dao,那么您根本没有使用服务层。在我的一些应用程序中也犯了同样的错误,我想知道“为什么服务层看起来这么丑陋,而且重复DAO”...

服务层应该是应用程序的接口,这并不意味着dao和service中的某些方法不相同,但主要部分显着不同。我不能在没有查看您的其余代码的情况下说出这一点,但通过您的问题(几个月前我的问题几乎与此相同),我觉得您正在使用贫血领域模型反模式。在贫血领域模型中,您的模型仅包含字段和getter,没有真正的方法(行为),这违反了基本的面向对象原则(对象==数据+行为)...您的行为可能在类似于服务层中的事务脚本中,但应该在您的模型(领域层)中。

通过使用丰富的领域模型(通过@Configurable注入到模型中的bean),可以解决这个问题。你可能会说,这违反了层次结构模式,你可能是正确的。但我相信,我们应该将我们的应用程序(领域+dao+服务)视为单个组件(请参见Alistair Cockburn的六边形架构/端口和适配器)。
然后,您的swing应用程序/ web客户端将成为核心组件的客户端,您可以在没有任何限制的情况下切换它们(因为所有修改数据的内容都在核心组件中)。
但是,这种方法有一个限制/缺点。如果您使用某种安全性(Spring Security)或通过Hibernate使用活动记录,则应通过DTO与所有客户端通信(而不是实体本身),因为当您联系实体时,它可能会调用服务,服务将通过事务激活自身并可以修改数据库(绕过您的安全性)。
希望我猜对了你的架构,如果没有,对于在此发明轮子的人,我感到抱歉,但是这篇文章可能会帮助一些不知道这一点的人(就像几个月前的我一样)。
编辑
针对您的编辑:即使在简单的CRUD应用程序中,某些操作也应该在服务层中完成 - 例如验证(不是验证“这是一个数字”,而是一些特定于业务的验证)。这不应该在您的视图中,因为如果您更改它,您将不得不再次复制和粘贴。当您查看您的代码时,您应该问一个问题:“如果我决定编写轻量级客户端(在Web浏览器中查看),是否有任何代码需要复制?” 如果答案是YES,则应为此可能的远程调用创建服务方法。
您应该/可以在服务层上执行的另一件事是授权(用户在这个角色中被允许删除此条目吗)。然后,您几乎必须为所有实体创建服务层,因为普通用户应该能够编辑(删除)他的条目,但可能不应该删除其他用户。但是管理员角色的用户可以这样做。
示例代码(我的应用程序中的服务接口的一部分(Spring Security)):
@Secured("ROLE_EDITOR")
public void save(ArticleDTO selectedArticle, ArticleDetailsDTO selectedArticleDetails);

在评论服务中,每个人都可以将他们的评论保存到文章中....
最后一点:你应该考虑是否真的需要服务层。如果以良好的方式编写,您的应用程序将在灵活性、可重用性和可维护性方面获得许多优点。但是编写它非常困难且耗时。如果您不想做所有这些事情(安全性、丰富的领域模型、从更多接口调用(更改视图实现)),则可以不使用它 :-)

非常感谢,我在我的第一篇帖子中添加了更多信息,请留下您的评论!谢谢! - blow
我的实体只包含getter和setter,但我的DAO包含的方法不仅仅是CRUD,因此它们是真正的存储库(根据DDD,驱动域设计)。当我编写实体时,我总是尝试以面向对象的方式思考。因此,我有一种贫血模型和领域模型的混合。 - blow
很好的链接,我之前不知道Fowler有一篇关于这个的文章。 - brabec

1

在某些时候,您的应用程序需要一些业务逻辑。此外,您可能希望验证输入,以确保没有请求恶意或性能不佳的内容。这些逻辑属于您的服务层。

此外,可能可以使您的DAO非常通用,只有一个或两个方法不会改变太多。这减少了每次想要添加/更改应用程序功能时对DAO类做错事情的风险。

DAO用于访问数据。服务用于业务逻辑。将它们分开,您将来会更加快乐。


0

不要强制执行

ui -> service -> DAO

对于每个操作,考虑允许两种选择

ui -> DAO
ui -> service -> DAO

后者用于更复杂的操作


0

最终,您需要协调多个DAO之间的行为。您还可以引入一些复杂性到您的业务规则中(例如:如果某个状态是特定的,则不要更新[此项])。这就是服务层派上用场的地方。

话虽如此,“技术上”完全没有问题,可以完全消除服务层。只是当您最终决定需要一个时,会更加痛苦。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接