Skip to content

Commit f37d697

Browse files
committed
iluwatar#590 explanation for Service Layer
1 parent 3e1a83e commit f37d697

File tree

1 file changed

+204
-6
lines changed

1 file changed

+204
-6
lines changed

service-layer/README.md

Lines changed: 204 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,210 @@ tags:
99
---
1010

1111
## Intent
12-
Service Layer is an abstraction over domain logic. Typically
13-
applications require multiple kinds of interfaces to the data they store and
14-
logic they implement: data loaders, user interfaces, integration gateways, and
15-
others. Despite their different purposes, these interfaces often need common
16-
interactions with the application to access and manipulate its data and invoke
17-
its business logic. The Service Layer fulfills this role.
12+
13+
Service Layer is an abstraction over domain logic. It defines application's boundary with a layer of services that
14+
establishes a set of available operations and coordinates the application's response in each operation.
15+
16+
## Explanation
17+
18+
Typically applications require different kinds of interfaces to the data they store and the logic they implement.
19+
Despite their different purposes, these interfaces often need common interactions with the application to access and
20+
manipulate its data and invoke its business logic. Encoding the logic of the interactions separately in each module
21+
causes a lot of duplication. It's better to centralize building the business logic inside single Service Layer to avoid
22+
these pitfalls.
23+
24+
Real world example
25+
26+
> We are writing an application that tracks wizards, spellbooks and spells. Wizards may have spellbooks and spellbooks
27+
may have spells.
28+
29+
In plain words
30+
31+
> Service Layer is an abstraction over application's business logic.
32+
33+
Wikipedia says
34+
35+
> Service layer is an architectural pattern, applied within the service-orientation design paradigm, which aims to
36+
organize the services, within a service inventory, into a set of logical layers. Services that are categorized into
37+
a particular layer share functionality. This helps to reduce the conceptual overhead related to managing the service
38+
inventory, as the services belonging to the same layer address a smaller set of activities.
39+
40+
**Programmatic Example**
41+
42+
The example application demonstrates interactions between a client `App` and a service `MagicService` that allows
43+
interaction between wizards, spellbooks and spells. The service is implemented with 3-layer architecture
44+
(entity, dao, service).
45+
46+
For this explanation we are looking at one vertical slice of the system. Let's start from the entity layer and look at
47+
`Wizard` class. Other entities not shown here are `Spellbook` and `Spell`.
48+
49+
```java
50+
@Entity
51+
@Table(name = "WIZARD")
52+
public class Wizard extends BaseEntity {
53+
54+
@Id
55+
@GeneratedValue
56+
@Column(name = "WIZARD_ID")
57+
private Long id;
58+
59+
private String name;
60+
61+
@ManyToMany(cascade = CascadeType.ALL)
62+
private Set<Spellbook> spellbooks;
63+
64+
public Wizard() {
65+
spellbooks = new HashSet<>();
66+
}
67+
68+
public Wizard(String name) {
69+
this();
70+
this.name = name;
71+
}
72+
73+
public Long getId() {
74+
return id;
75+
}
76+
77+
public void setId(Long id) {
78+
this.id = id;
79+
}
80+
81+
public String getName() {
82+
return name;
83+
}
84+
85+
public void setName(String name) {
86+
this.name = name;
87+
}
88+
89+
public Set<Spellbook> getSpellbooks() {
90+
return spellbooks;
91+
}
92+
93+
public void setSpellbooks(Set<Spellbook> spellbooks) {
94+
this.spellbooks = spellbooks;
95+
}
96+
97+
public void addSpellbook(Spellbook spellbook) {
98+
spellbook.getWizards().add(this);
99+
spellbooks.add(spellbook);
100+
}
101+
102+
@Override
103+
public String toString() {
104+
return name;
105+
}
106+
}
107+
```
108+
109+
Above the entity layer we have DAOs. For `Wizard` the DAO layer looks as follows.
110+
111+
```java
112+
public interface WizardDao extends Dao<Wizard> {
113+
114+
Wizard findByName(String name);
115+
}
116+
117+
public class WizardDaoImpl extends DaoBaseImpl<Wizard> implements WizardDao {
118+
119+
@Override
120+
public Wizard findByName(String name) {
121+
Transaction tx = null;
122+
Wizard result;
123+
try (var session = getSessionFactory().openSession()) {
124+
tx = session.beginTransaction();
125+
var criteria = session.createCriteria(persistentClass);
126+
criteria.add(Restrictions.eq("name", name));
127+
result = (Wizard) criteria.uniqueResult();
128+
tx.commit();
129+
} catch (Exception e) {
130+
if (tx != null) {
131+
tx.rollback();
132+
}
133+
throw e;
134+
}
135+
return result;
136+
}
137+
}
138+
```
139+
140+
Next we can look at the Service Layer, which in our case consists of a single `MagicService`.
141+
142+
```java
143+
public interface MagicService {
144+
145+
List<Wizard> findAllWizards();
146+
147+
List<Spellbook> findAllSpellbooks();
148+
149+
List<Spell> findAllSpells();
150+
151+
List<Wizard> findWizardsWithSpellbook(String name);
152+
153+
List<Wizard> findWizardsWithSpell(String name);
154+
}
155+
156+
public class MagicServiceImpl implements MagicService {
157+
158+
private WizardDao wizardDao;
159+
private SpellbookDao spellbookDao;
160+
private SpellDao spellDao;
161+
162+
public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) {
163+
this.wizardDao = wizardDao;
164+
this.spellbookDao = spellbookDao;
165+
this.spellDao = spellDao;
166+
}
167+
168+
@Override
169+
public List<Wizard> findAllWizards() {
170+
return wizardDao.findAll();
171+
}
172+
173+
@Override
174+
public List<Spellbook> findAllSpellbooks() {
175+
return spellbookDao.findAll();
176+
}
177+
178+
@Override
179+
public List<Spell> findAllSpells() {
180+
return spellDao.findAll();
181+
}
182+
183+
@Override
184+
public List<Wizard> findWizardsWithSpellbook(String name) {
185+
var spellbook = spellbookDao.findByName(name);
186+
return new ArrayList<>(spellbook.getWizards());
187+
}
188+
189+
@Override
190+
public List<Wizard> findWizardsWithSpell(String name) {
191+
var spell = spellDao.findByName(name);
192+
var spellbook = spell.getSpellbook();
193+
return new ArrayList<>(spellbook.getWizards());
194+
}
195+
}
196+
```
197+
198+
And finally we can show how the client `App` interacts with `MagicService` in the Service Layer.
199+
200+
```java
201+
var service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao);
202+
LOGGER.info("Enumerating all wizards");
203+
service.findAllWizards().stream().map(Wizard::getName).forEach(LOGGER::info);
204+
LOGGER.info("Enumerating all spellbooks");
205+
service.findAllSpellbooks().stream().map(Spellbook::getName).forEach(LOGGER::info);
206+
LOGGER.info("Enumerating all spells");
207+
service.findAllSpells().stream().map(Spell::getName).forEach(LOGGER::info);
208+
LOGGER.info("Find wizards with spellbook 'Book of Idores'");
209+
var wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores");
210+
wizardsWithSpellbook.forEach(w -> LOGGER.info("{} has 'Book of Idores'", w.getName()));
211+
LOGGER.info("Find wizards with spell 'Fireball'");
212+
var wizardsWithSpell = service.findWizardsWithSpell("Fireball");
213+
wizardsWithSpell.forEach(w -> LOGGER.info("{} has 'Fireball'", w.getName()));
214+
```
215+
18216

19217
## Class diagram
20218
![alt text](./etc/service-layer.png "Service Layer")

0 commit comments

Comments
 (0)