【问题标题】:Where should business logic (and what is that?) really live and how to do that with Spring?业务逻辑应该在哪里(以及那是什么?)真正存在以及如何使用 Spring 做到这一点?
【发布时间】:2012-07-26 06:59:13
【问题描述】:

我刚刚在看这篇文章:

http://www.tutorialized.com/view/tutorial/Spring-MVC-Application-Architecture/11986

我觉得很棒。它很好地解释了层架构,我很高兴我正在使用的架构几乎就是他所描述的。

但有一件事,我似乎没有明白:

首先:什么是业务逻辑,什么不是?他在文章中说(而且他不是唯一一个),业务逻辑应该放在领域模型中。所以Account 类应该有一个知道如何激活Accountactivate() 方法。据我了解,这可能涉及一些持久性工作。但是领域模型不应该依赖于 DAO。只有服务层应该知道 DAO。

那么,业务逻辑是否只是领域实体可以对自身执行的操作?就像activate()方法将active属性设置为true,加上dateActivated属性设置为new Date(),然后服务的任务是首先调用account.activate()和第二个dao.saveAccount(account)?服务需要什么外部依赖项?到目前为止,我大部分时间都是这样做的。

public AccountServiceImpl implements AccountService
{
  private AccountDAO dao;
  private MailSender mailSender;

  public void activateAccount(Account account)
  {
     account.setActive(true);
     account.setDateActivated(new Date());
     dao.saveAccount(account);
     sendActivationEmail(account);
  }
  private void sendActivationEmail(Account account)
  {
    ...
  }
}

我认为这与他所说的相反,不是吗?

我也没有得到关于如何拥有像 Account 这样的 Spring 线域对象的示例。如果 Account 自己发送电子邮件,则需要哪个。

鉴于此代码:

import org.springframework.mail.MailSender;  
import org.springframework.mail.SimpleMailMessage;  

public class Account {  
   private String email;  
   private MailSender mailSender;  
   private boolean active = false;  

   public String getEmail() {  
      return email;  
   }  

   public void setEmail(String email) {  
      this.email = email;  
   }  

   public void setMailSender(MailSender mailSender) {  
      this.mailSender = mailSender;  
   }  

   public void activate() {  
      if (active) {  
      throw new IllegalStateException("Already active");  
      }  

      active = true;  
      sendActivationEmail();  
   }  

   private void sendActivationEmail() {  
      SimpleMailMessage msg = new SimpleMailMessage();  
      msg.setTo(email);  
      msg.setSubject("Congrats!");  
      msg.setText("You're the best.");  
      mailSender.send(msg);  
   }  
}

如果我使用 Hibernate,我可以使用DependencyInjectionInterceptorFactoryBean 来连接mailSender。如果我改用 JDBC,我真的会编写以下繁琐的代码吗?另外,当我在 MVC 控制器中为 Account 创建一个新实例时,比如说将其填充到模型中??

  BeanFactory beanFactory = new XmlBeanFactory(  
     new ClassPathResource("chapter3.xml"));  
  Account account = new Account();  
  account.setEmail("email@example.com");  
  ((AutowireCapableBeanFactory)beanFactory).applyBeanPropertyValues(  
        account, "accountPrototype");  
  account.activate(); 

这不可靠而且很麻烦,不是吗?每当我看到 Account 的实例时,我都会问自己该对象是在哪里创建的。另外,如果我采用这种方法:我没有一个可以通过的 appContext.xml,而是几个,一个用于持久性,一个用于服务配置。我该怎么做?另外,每次创建这样的实例时都会创建一个全新的上下文,还是我遗漏了什么?

没有更好的解决办法吗?

非常感谢任何帮助。

【问题讨论】:

    标签: java spring dependency-injection business-logic business-objects


    【解决方案1】:

    我认为 send activation email 操作在这里不是业务层的一部分,您的域逻辑是 account activation 操作,该逻辑应该存在在名称为 AccountDomainObject 中(activate() 方法)。 发送激活电子邮件 操作是infrastructureapplication 层的一部分。

    Service是处理账户激活请求和连接业务层等的对象。服务获取给定帐户,激活它们并执行 MailSenderService发送激活电子邮件 操作或类似的操作。

    小样:

    public AccountServiceImpl implements AccountService
    {
     private AccountDAO dao;
     private MailSenderService mailSender;
    
     public void activateAccount(AccountID accountID)
     {
       Account account = dao.findAccount(accountID);
       ....
       account.activate();
       dao.updateAccount(account);
       ....
    
       mailSender.sendActivationEmail(account);
     }
    
    }
    

    我可以建议的下一步是完全分离业务层 和一层基础设施。这可以通过引入 商务活动。服务不再需要执行操作来发送 电子邮件,它会创建事件通知其他层 帐户激活。

    在 Spring 中,我们有两个工具来处理事件,ApplicationEventPublisherApplicationListener

    简短的例子,发布领域事件的服务:

    public AccountActivationEvent extends ApplicationEvent {
        private Account account;
    
        AccountActivationEvent(Account account) {
           this.account = account;
        }
    
        public Account getActivatedAccount() {
           return account;
        }
    }
    
    public AccountServiceImpl implements AccountService, ApplicationEventPublisherAware
    {
     private AccountDAO dao;
     private ApplicationEventPublisher epublisher;
    
     public void setApplicationEventPublisher(ApplicationEventPublisher epublisher) {
        this.epublisher = epublisher;
     }
    
     public void activateAccount(AccountID accountID)
     {
      Account account = dao.findAccount(accountID);
      ....
      account.activate();
      dao.updateAccount(account);
      ....
    
      epublisher.publishEvent(new AccountActivationEvent(account));
     }
    
    }
    

    还有领域事件监听器,在基础设施层:

    public class SendAccountActivationEmailEventListener 
          implements ApplicationListener<AccountActivationEvent> {
    
      private MailSenderService mailSender;
    
      .... 
    
      public final void onApplicationEvent(final AccountActivationEvent event) {
       Account account = event.getActivatedAccount():
        .... perform mail ...
       mailSender.sendEmail(email);
      }
     }
    

    现在您可以添加其他激活类型、日志记录、其他基础设施支持而无需更改并污染您的域(业务)层。

    啊,你可以在the documentation了解更多关于春季活动的信息。

    【讨论】:

    • mastered,感谢您的回答。所以,基本上我的建议是正确的,业务逻辑是“业务实体可以自己完成的东西,无需使用其他层”。其余的是服务任务(或基础设施或任何你称之为的)。对我来说有点道理,但直觉上,我会将是否发送确认电子邮件的决定称为“商业决策”,我认为这让我感到困惑。
    • 是否发送确认电子邮件的决定 - 是的,是“业务决策”,但发送不是业务任务。 :) 而且我不确定该决定是帐户的职责范围。这很可能是 AccountService 的任务。
    猜你喜欢
    • 2020-06-29
    • 2018-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 2012-10-06
    • 1970-01-01
    • 2019-01-30
    相关资源
    最近更新 更多