Skip to content

目的

服务层是对领域逻辑的抽象。它用一层服务定义应用程序的边界,从而建立一组可用的操作,并在每个操作中协调应用程序的响应。

解释

通常,应用程序需要不同类型的接口来连接存储的数据和实现的逻辑。尽管这些接口的用途不同,但它们通常需要与应用程序进行公开交互才能访问、操纵其数据并调用其业务逻辑。在每个模块中分别对交互逻辑进行编码导致了大量重复。因此最好集中在单个服务层内构建业务逻辑,以避免这些陷阱。

真实世界案例

我们正在编写一个跟踪巫师、咒语书和咒语的应用程序。巫师可能有魔法书,魔法书可能有魔法。

简单地说

服务层是对应用程序业务逻辑的抽象。

维基百科说

服务层是一种架构模式,应用于面向服务的设计模式中,其目的是将服务清单中的服务组织到一组逻辑层面中。归类到特定层的服务可以共享功能。这有助于减少与管理服务相关的概念性开销,因为属于同一层服务处理的活动较少。

编程示例

示例应用程序演示了客户端 App 和服务 MagicService 之间的交互,该服务允许巫师、咒语书和咒语之间的互动。该服务采用三层体系结构实现(实体层、DAO层、服务层)。

对于这一解释,我们关注的是系统的一个垂直部分。 让我们从实体层开始,看看 Wizard 类。 没有展示在这里的实体类是 SpellbookSpell

java
@Entity
@Table(name = "WIZARD")
public class Wizard extends BaseEntity {

  @Id
  @GeneratedValue
  @Column(name = "WIZARD_ID")
  private Long id;

  private String name;

  @ManyToMany(cascade = CascadeType.ALL)
  private Set<Spellbook> spellbooks;

  public Wizard() {
    spellbooks = new HashSet<>();
  }

  public Wizard(String name) {
    this();
    this.name = name;
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Set<Spellbook> getSpellbooks() {
    return spellbooks;
  }

  public void setSpellbooks(Set<Spellbook> spellbooks) {
    this.spellbooks = spellbooks;
  }

  public void addSpellbook(Spellbook spellbook) {
    spellbook.getWizards().add(this);
    spellbooks.add(spellbook);
  }

  @Override
  public String toString() {
    return name;
  }
}

在实体层之上,我们有DAO层。对于Wizard,DAO层如下所示。

java
public interface WizardDao extends Dao<Wizard> {

  Wizard findByName(String name);
}

public class WizardDaoImpl extends DaoBaseImpl<Wizard> implements WizardDao {

  @Override
  public Wizard findByName(String name) {
    Transaction tx = null;
    Wizard result;
    try (var session = getSessionFactory().openSession()) {
      tx = session.beginTransaction();
      var criteria = session.createCriteria(persistentClass);
      criteria.add(Restrictions.eq("name", name));
      result = (Wizard) criteria.uniqueResult();
      tx.commit();
    } catch (Exception e) {
      if (tx != null) {
        tx.rollback();
      }
      throw e;
    }
    return result;
  }
}

接下来可以看看服务层,在我们的例子中,它由一个MagicService组成。

java
public interface MagicService {

  List<Wizard> findAllWizards();

  List<Spellbook> findAllSpellbooks();

  List<Spell> findAllSpells();

  List<Wizard> findWizardsWithSpellbook(String name);

  List<Wizard> findWizardsWithSpell(String name);
}

public class MagicServiceImpl implements MagicService {

  private final WizardDao wizardDao;
  private final SpellbookDao spellbookDao;
  private final SpellDao spellDao;

  public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) {
    this.wizardDao = wizardDao;
    this.spellbookDao = spellbookDao;
    this.spellDao = spellDao;
  }

  @Override
  public List<Wizard> findAllWizards() {
    return wizardDao.findAll();
  }

  @Override
  public List<Spellbook> findAllSpellbooks() {
    return spellbookDao.findAll();
  }

  @Override
  public List<Spell> findAllSpells() {
    return spellDao.findAll();
  }

  @Override
  public List<Wizard> findWizardsWithSpellbook(String name) {
    var spellbook = spellbookDao.findByName(name);
    return new ArrayList<>(spellbook.getWizards());
  }

  @Override
  public List<Wizard> findWizardsWithSpell(String name) {
    var spell = spellDao.findByName(name);
    var spellbook = spell.getSpellbook();
    return new ArrayList<>(spellbook.getWizards());
  }
}

最后,我们可以展示客户端App如何在服务层与MagicService交互。

java
    var service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao);
    LOGGER.info("Enumerating all wizards");
    service.findAllWizards().stream().map(Wizard::getName).forEach(LOGGER::info);
    LOGGER.info("Enumerating all spellbooks");
    service.findAllSpellbooks().stream().map(Spellbook::getName).forEach(LOGGER::info);
    LOGGER.info("Enumerating all spells");
    service.findAllSpells().stream().map(Spell::getName).forEach(LOGGER::info);
    LOGGER.info("Find wizards with spellbook 'Book of Idores'");
    var wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores");
    wizardsWithSpellbook.forEach(w -> LOGGER.info("{} has 'Book of Idores'", w.getName()));
    LOGGER.info("Find wizards with spell 'Fireball'");
    var wizardsWithSpell = service.findWizardsWithSpell("Fireball");
    wizardsWithSpell.forEach(w -> LOGGER.info("{} has 'Fireball'", w.getName()));

类图

alt text

应用

在以下需要时使用服务层模式:

  • 您想在API下封装域逻辑
  • 您需要使用公共逻辑和数据实现多个接口

鸣谢