-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
522 lines (425 loc) · 16.8 KB
/
index.html
File metadata and controls
522 lines (425 loc) · 16.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Hexagonal Architecture in practice</title>
<link rel="stylesheet" href="dist/reset.css">
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/night.css">
<!-- Theme used for syntax highlighted code -->
<link rel="stylesheet" href="plugin/highlight/monokai.css">
<link rel="stylesheet" href="css/custom.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-markdown data-separator-vertical="^--$">
<script type="text/template">
# Hexagonal Architecture
## in practice<!-- .element class="pink"-->
---
## Agenda
### Day 1
* Start: 9:00
* Introduction session with 10 minutes break
* Lunch break: 12:00 - 13:00
* Coding session with 30 minutes break
* Session end 17:00
--
## Agenda
### Day 2
* Start: 9:00
* Data mapping session with 10 minutes break
* Lunch break: 12:00 - 13:00
* Coding session with 10 minutes break
* Coffee break: 15:30 - 16:00
* Closing session with Q&A
* Session end 17:00
---
<!-- .slide: id="why-architecture"-->
Why architecture?<!-- .element: class="r-fit-text"-->
--
What do you see?

--
[Design Stamina Hypotesis, M. Fowler](https://martinfowler.com/bliki/DesignStaminaHypothesis.html)

--
### Key takeaways
* <mark>Good design</mark> makes code maintainable
* Good design does not increase productivity
* You can save short-term time by neglecting design (producing technical debt, though)
--
### <mark>Good architecture</mark> makes the code maintainable too
* Good architecture separates concerns well
* Key point is to keep business code separated from technical one
* Business code is getting older slower than the technical one
---

--
<!-- .element: height="500"-->
---
### Layered vs hexagonal architecture
--
### Three-Layers architecture (classical view)

--
### Three-Layers architecture (hexagonal view)

--
### Hexagonal architecture

---
### Hexagonal architecture
#### aka "Ports and Adapters"
Alistair Cockburn, 2005
https://alistair.cockburn.us/hexagonal-architecture/
--
What is <mark>hexagonal architecture</mark>?

--
What is <mark>hexagonal architecture</mark>?

UML 2.0 Component
--
What is <mark>hexagonal architecture</mark>?

--
What is the <mark>core</mark>?

--
Dependencies point <mark>inwards</mark>

--
Flow of control

--
Summary

--
### <mark>Key advantages</mark>
#### of Hexagonal Architecture
* <span class="pink">exposes domain</span> as the most important element
* <span class="pink">separates</span> technical and business aspects
* <span class="pink">screams out</span> the purpose of the system
---
# Let's code
--
### Document Management System
preparation<!-- .element: class="cyan"-->

--
### Document Management System
verification<!-- .element: class="cyan"-->

--
### Document Management System
signing documents<!-- .element: class="cyan"-->

--
### Domain Driven Design
event storming elements<!-- .element: class="cyan"-->

--
### Domain Driven Design
event storming<!-- .element: class="cyan"-->

--
### Bounded context

https://martinfowler.com/bliki/BoundedContext.html
--
### Bounded context
identification hints<!-- .element: class="cyan"-->
* <mark>METAMORPHOSIS</mark> same name - different behaviour
* <mark>HANDOFF</mark> passes or delegates to another system (user)
* <mark>UTILITY</mark> "common" tasks
--
### Domain Driven Design
bounded contexts<!-- .element: class="cyan"-->

--
### <mark>DDD</mark> key words
* bounded context
* subdomain
* aggregate
* ubiquitous language
* command, query, event
--
### Hexagonal Architecture & DDD

* DDD discovers _business architecture_ elements
* Hexagonal Architecture promotes <mark>domain</mark>
<!-- .element class="fragment"-->
--
Business components in our code

--
Anatomy of component

---
<!-- .slide: id="live-coding-1"-->
### Live coding 1
implementing `QueryRevisionById`
--
### Summary
* Inbound web adapter using read model ✔
* Outbound persistence adapter ✔
* `QueryRevisionById` Service ✔
* Subdomains visible as first level of directories (<mark>screaming</mark>) ✔
---
<!-- .slide: id="exercise-1"-->
### Exercise 1
implement `CreateRevision` with tests
--
### Summary
1. Inbound web adapter using command pattern ✔
2. Outbound persistence adapter ✔
3. `CreateRevision` Service ✔
4. ... or switch to branch `wks-02-second-usecase-prep`
---
<!-- .slide: id="mapping"-->
Mapping between boundaries<!-- .element class="r-fit-text"-->
--
### What is <mark>mapping</mark>?
> Mapping is a function that transforms data from one data structure to another data structure.
<em>Mapping</em> is necessary when two layers use separate data models.
--
Potential mapping points in Hexagonal Architecture

--
### Mapping methods
1. No mapping
1. Two way mapping
1. Full mapping
1. One way mapping
--
### 1. No mapping

<p><mark>Example</mark> JPA based db adapter and core share the same model.</p><!-- .element: class="subtitle"-->
```java[]
public void archiveRevision(RevisionId id) {
var revision = queryRevisionByIdPort.query(id);
revision.archive();
saveRevisionPort.save(revision);
}
...
public void save(Revision revision) {
revisionRepository.save(revision);
}
```
--
### 1. No mapping
<ul class="left-align pink no-ticks">
<li>✔ no hassle to do the mapping</li>
<li>✔ changes are automatically propagated</li>
<li>✘ lack of separation - layers are tangled</li>
</ul>
--
### 2. Two way mapping

<p><mark>Example</mark> JPA based db adapter has different model than core</p><!-- .element: class="subtitle"-->
```java[]
public void archiveRevision(RevisionId id) {
var revision = queryRevisionByIdPort.query(id);
revision.archive();
saveRevisionPort.save(revision);
}
...
public void save(Revision revision) {
var revisionRecord = revisionRepository.findById(revision.id());
mapper.map(revisionRecord, revision);
revisionRepository.save(revisionRecord);
}
```
--
### 2. Two way mapping
<ul class="left-align pink no-ticks">
<li>✔ full separation of layers</li>
<li>✘ structures may be still semantically tangled</li>
<li>✘ extra cost of implementing mappers</li>
<li>✔ mapping cost reduced due to reusability</li>
<li>✘ single change triggers multiple changes in use cases</li>
</ul>
--
### 3. Full mapping

<p><mark>Example</mark> Use separate model per use case in changing use cases</p><!-- .element: class="subtitle"-->
```java[]
public void createRevision(CreateRevisionCommand command) {
var revision = Revision.create(command.title(), command.content());
saveRevisionPort.save(revision);
}
```
--
### 3. Full mapping
<ul class="left-align cyan no-ticks">
<li>✔ full separation of layers</li>
<li>✔ full separation of use cases</li>
<li>✔ low risk of semantical tangling</li>
<li>✘ a lot of mapping code and extra data structures</li>
</ul>
--
### Heuristics
When to use which mapping method?
_by Tom Hombergs_
1. We can use different strategies between inbound and core and between core and outbound.<!-- .element class="cyan"-->
2. We can use different strategies for reading and modifying use cases.<!-- .element class="pink"-->
--
### Heuristic #1
> When working on <mark>modifying use case</mark>, the "full mapping" (aka command pattern)
> is the first choice for <mark>web-core</mark> boundary.
<ul class="left-align cyan no-ticks">
<li>✔ clear use case separation</li>
<li>✔ only needed fields are passed</li>
</ul>
--
### Heuristic #2
> When working on <mark>modifying use case</mark>, the "no mapping"
> is the first choice for <mark>core-db</mark> boundary.
<ul class="left-align cyan no-ticks">
<li>✔ quickly evolve code without mapping overhead</li>
<li>✔ can be turned into "two-way" as soon as persistence details start to leak</li>
</ul>
--
### Heuristic #3
> When working on <mark>a query</mark>, the "no mapping" is thee first choice
> for <mark>web-core</mark> and <mark>core-db</mark> boundaries.
<ul class="left-align cyan no-ticks">
<li>✔ quickly evolve code without mapping overhead</li>
<li>✔ can be turned into "two-way" as soon as persistence details start to leak</li>
</ul>
--
### 4. One-way mapping

--
### 4. One-way mapping
All models in all (or some) layers implement the same interface.
<ul class="left-align pink no-ticks">
<li>✔ no additional mappers are needed</li>
<li>✔ still separation is possible thanks to non public parts</li>
<li>✘ tight coupling of data structures</li>
</ul>
Fits well if data structures across layers are similar but "no mapping" is by some reason not suitable.
---
Mapping examples<!-- .element class="r-fit-text"-->
--
Example for <mark>master data</mark> service

--
Example for typical <mark>DevonFW</mark> service

--
# YES!
> <span class="cyan">DevonFW</span> (layered architecture) is an instance of hexagonal architecture,
> with <span class="pink">preselected</span> mapping methods.<sup>*</sup>
<p><sup>*</sup>well... almost. You need to move entity
declaration and repository interfaces from <code>dataaccess</code> to <code>core</code></p><!-- .element: class="fragment"-->
--
Example for <mark>deep system</mark> using <mark>CQRS</mark> style

--
Example for <mark>event sourcing</mark>

---
<!-- .slide: id="live-coding-2"-->
### Live coding 2
1. Implementing `RequestVerification`
2. Communication between subdomains using <mark>events</mark>
--
### Summary

1. Inbound web adapter ✔
1. Outbound event adapter ✔
1. Inbound event adapter ✔
1. Outbound persistence adapter ✔
1. `PushRevisionToVerification` Service ✔
---
Subdomains can talk synchronously as well

--
<!-- .slide: id="exercise-2"-->
### Exercise 2
Implement `QueryRevisionPreview`
--
### Summary
1. Set up dependency between `verification` and `preparation` ✔
2. Inbound web adapter for preview ✔
3. Outbound adapter to call preparation use case ✔
---
### Hexagonal architecture in a nutshell
<!-- * Start from hex and then compare with layers
* DDD & CQRS (Herberto Graca)
* Shallow vs deep systems -->
https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/
<!-- --
### Different architecture style
Three examples:
* Everything in one class
* Layered approach
* Hexagonal approach -->
---
## Backup
---
<!-- .slide: id="screaming"-->
Screaming architecture<!-- .element class="r-fit-text"-->
or how to organize files using subdirectories<!-- .element: class="pink"-->
--
> When you look at the top-level directory structure,
> and the source files in the highest-level package,
> do they scream "Health Care System" or "Accounting system"
> or "Inventory Management System"?
Robert C. Martin,
Clean Architecture: A Craftsman's Guide to Software Structure and Design<!-- .element class="cyan"-->
--
### What does this system do?
<div class="r-hstack gap1">
<img src="assets/my-thai-no-screaming.png" alt="No screaming example">
<img src="assets/my-thai-subdomains.png" alt="Subdomains" class="fragment">
</div>
<!-- .element class="fragment"-->
--
### Why is this a problem?
* <mark>Technology</mark> overshadows <mark>business</mark>
* Harder to onboard new developers
* Imagine we have 20+ microservices...
* Can architecture tell programmers <mark>where to put new code</mark>, ...?
--
### What does this system do?

--
### Can we scream louder?
Hexagonal architecture

--
Make entities and use cases first class citizens

--
Most important parts should be the easiest to reach

--
Bury less important parts into subdirectories

</script>
</section>
</div>
</div>
<script src="dist/reveal.js"></script>
<script src="plugin/notes/notes.js"></script>
<script src="plugin/markdown/markdown.js"></script>
<script src="plugin/highlight/highlight.js"></script>
<script>
// More info about initialization & config:
// - https://revealjs.com/initialization/
// - https://revealjs.com/config/
Reveal.initialize({
hash: true,
width: 1200,
slideNumber: 'c/t',
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
});
</script>
</body>
</html>