目的
服务层是对领域逻辑的抽象。它用一层服务定义应用程序的边界,从而建立一组可用的操作,并在每个操作中协调应用程序的响应。
解释
通常,应用程序需要不同类型的接口来连接存储的数据和实现的逻辑。尽管这些接口的用途不同,但它们通常需要与应用程序进行公开交互才能访问、操纵其数据并调用其业务逻辑。在每个模块中分别对交互逻辑进行编码导致了大量重复。因此最好集中在单个服务层内构建业务逻辑,以避免这些陷阱。
真实世界案例
我们正在编写一个跟踪巫师、咒语书和咒语的应用程序。巫师可能有魔法书,魔法书可能有魔法。
简单地说
服务层是对应用程序业务逻辑的抽象。
维基百科说
服务层是一种架构模式,应用于面向服务的设计模式中,其目的是将服务清单中的服务组织到一组逻辑层面中。归类到特定层的服务可以共享功能。这有助于减少与管理服务相关的概念性开销,因为属于同一层服务处理的活动较少。
编程示例
示例应用程序演示了客户端 App 和服务 MagicService 之间的交互,该服务允许巫师、咒语书和咒语之间的互动。该服务采用三层体系结构实现(实体层、DAO层、服务层)。
对于这一解释,我们关注的是系统的一个垂直部分。 让我们从实体层开始,看看 Wizard 类。 没有展示在这里的实体类是 Spellbook 和 Spell。
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()));类图

应用
在以下需要时使用服务层模式:
- 您想在API下封装域逻辑
- 您需要使用公共逻辑和数据实现多个接口