Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
2
Share docs with
other systems
3
Which protocol?
4
5
1 month later…
6
7
Done!
8
1 month later…
9
10
Done!
11
+
12
The secrets of
13
Hexagonal

Architecture
Separate Domain
& Infrastructure
14
Maintainable Software
=
Domain 

vs
Infrastructure
Twitter 

GitHub 

Medium
16
}@nicoespeon
Nicolas Carlo
17
80 countries
18
Seat
Stop
Departure
Roundtrip
Leg
Taxes
Discount code
Fees
Domain is our
business
19
We could do our
business without
software!
20
21
Infrastructure is
how we make it work
22
Infrastructure 

is a detail
23
Put the Domain
at the heart of
the software
24
Problems mixing
Domain & Infra.
Get a poem from a
poetry library, or return
the default one.
26
27
class PoetryReader {
giveMeSomePoetry(): string {
}
}
28
import * as fs from "fs";
import * as path from "path";
class PoetryReader {
giveMeSomePoetry(): string {
const pathToP...
29
import * as fs from "fs";
import * as path from "path";
class PoetryReader {
giveMeSomePoetry(): string {
const pathToP...
30
import * as fs from "fs";
import * as path from "path";
class PoetryReader {
giveMeSomePoetry(): string {
const pathToP...
31
import * as fs from "fs";
import * as path from "path";
class PoetryReader {
giveMeSomePoetry(): string {
const pathToP...
32
import * as fs from "fs";
import * as path from "path";
class PoetryReader {
giveMeSomePoetry(): string {
const pathToP...
33
import * as fs from "fs";
import * as path from "path";
class PoetryReader {
giveMeSomePoetry(): string {
const pathToP...
const pathToPoem = path.join(!__dirname, "poem.txt");
let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" });
Hard t...
const pathToPoem = path.join(!__dirname, "poem.txt");
let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" });
Hard t...
const pathToPoem = path.join(!__dirname, "poem.txt");
let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" });
Hard t...
37
import FileReader from "lib/file-reader";
class PoetryReader {
giveMeSomePoetry(): string {
let poem = FileReader.read(...
import FileReader from "lib/file-reader";
class PoetryReader {
giveMeSomePoetry(): string {
let poem = FileReader.read("po...
Hexagonal
Architecture
The simplest way to
40
Separate Domain 

& Infrastructure
41
Infra.
Domain
42
Infra.
Domain
dependency
43
Infra.
Domain
dependency
44
Interface
Adapter
use
implement
45
Port
Adapter
46
Port
Adapter
Ports & Adapters architecture
47
Business
language

only
48
Left-side Right-side
49
Left-side Right-side
Adapter
Adapter
50
Left-side Right-side
Adapter
Adapter
Adapter
Adapter
A concrete
example
class PoetryReader {
}
Domain
52
class PoetryReader {
poetryLibrary: ObtainPoems;
constructor(poetryLibrary!?: ObtainPoems) {
this.poetryLibrary = poetryLi...
class PoetryReader {
poetryLibrary: ObtainPoems;
constructor(poetryLibrary!?: ObtainPoems) {
this.poetryLibrary = poetryLi...
class PoetryReader {
poetryLibrary: ObtainPoems;
constructor(poetryLibrary!?: ObtainPoems) {
this.poetryLibrary = poetryLi...
class PoetryReader {
poetryLibrary: ObtainPoems;
constructor(poetryLibrary!?: ObtainPoems) {
this.poetryLibrary = poetryLi...
giveMeSomePoetry(): Poem {
if (!this.poetryLibrary) {
return "If you could read a leaf or treernyou’d have
no need of book...
Domain
type Poem = string;
interface ObtainPoems {
getAPoem(): Poem
}
58
import * as fs from "fs";
import * as path from "path";
import ObtainPoems from "!../domain/obtain-poems";
class ObtainPoe...
!// 1. Instantiate the right-side adapter(s)
!// 2. Instantiate the hexagon (domain)
!// 3. Instantiate the left-side adap...
import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs";
!// 1. Instantiate the right-side adapter(s)
const ...
import PoetryReader from "./domain/poetry-reader";
import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs";
...
import PoetryReader from "./domain/poetry-reader";
import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs";
...
import PoetryReader from "./domain/poetry-reader";
import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs";
...
const poetryReader = new PoetryReader();
65
const poetryReader = new PoetryReader(poetryLibrary);
66
Pros and cons
This ain't new
68
"Program to an Interface" − OOP
"Isolate side effects" − FP
The Onion Architecture
Plug different adapters
to the Domain
69
A good architect
defers decisions
70
Start with
something simple
71
But… it's only
the first step!
72
73
More layers
74
Ubiquitous Language 

Bounded Contexts

Domain modelling
Separate Domain
& Infrastructure
75
🙏
Twitter 

GitHub 

Medium
}@nicoespeon
Nicolas Carlo
@swcraftmontreal
Software Crafters Montréal
Every 1st Tuesday of the month.
Bonuses
test("give verses when asked for poetry", () !=> {
const poetryReader = new PoetryReader();
const verses = poetryReader.gi...
test("give verses from a PoetryLibrary", () !=> {
const poetryLibrary: ObtainPoems = {
getAPoem() {
return "I want to slee...
The Secrets of Hexagonal Architecture
Upcoming SlideShare
Loading in …5
×

The Secrets of Hexagonal Architecture

445 views

Published on

Slides of the talk I gave at ConFoo Montréal 2019 about "Hexagonal Architecture".

Hexagonal Architecture, Clean Architecture, Domain-Driven Design… You may have heard about them. Let's start from scratch in this session and go beyond the buzzwords. We'll go together though these ideas to understand how you can improve the maintainability of your projects', either greenfield or legacy.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

The Secrets of Hexagonal Architecture

  1. 1. 2
  2. 2. Share docs with other systems 3
  3. 3. Which protocol? 4
  4. 4. 5
  5. 5. 1 month later… 6
  6. 6. 7
  7. 7. Done! 8
  8. 8. 1 month later… 9
  9. 9. 10
  10. 10. Done! 11
  11. 11. + 12
  12. 12. The secrets of 13 Hexagonal
 Architecture
  13. 13. Separate Domain & Infrastructure 14 Maintainable Software =
  14. 14. Domain 
 vs Infrastructure
  15. 15. Twitter 
 GitHub 
 Medium 16 }@nicoespeon Nicolas Carlo
  16. 16. 17 80 countries
  17. 17. 18 Seat Stop Departure Roundtrip Leg Taxes Discount code Fees
  18. 18. Domain is our business 19
  19. 19. We could do our business without software! 20
  20. 20. 21
  21. 21. Infrastructure is how we make it work 22
  22. 22. Infrastructure 
 is a detail 23
  23. 23. Put the Domain at the heart of the software 24
  24. 24. Problems mixing Domain & Infra.
  25. 25. Get a poem from a poetry library, or return the default one. 26
  26. 26. 27 class PoetryReader { giveMeSomePoetry(): string { } }
  27. 27. 28 import * as fs from "fs"; import * as path from "path"; class PoetryReader { giveMeSomePoetry(): string { const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); } }
  28. 28. 29 import * as fs from "fs"; import * as path from "path"; class PoetryReader { giveMeSomePoetry(): string { const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } }
  29. 29. 30 import * as fs from "fs"; import * as path from "path"; class PoetryReader { giveMeSomePoetry(): string { const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } }
  30. 30. 31 import * as fs from "fs"; import * as path from "path"; class PoetryReader { giveMeSomePoetry(): string { const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } } Domain
  31. 31. 32 import * as fs from "fs"; import * as path from "path"; class PoetryReader { giveMeSomePoetry(): string { const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } } Infrastructure
  32. 32. 33 import * as fs from "fs"; import * as path from "path"; class PoetryReader { giveMeSomePoetry(): string { const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } }
  33. 33. const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); Hard to 
 see the Business 34
  34. 34. const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); Hard to 
 test the Business 35
  35. 35. const pathToPoem = path.join(!__dirname, "poem.txt"); let poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); Hard to 
 change the Business 36
  36. 36. 37 import FileReader from "lib/file-reader"; class PoetryReader { giveMeSomePoetry(): string { let poem = FileReader.read("poem.txt"); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } }
  37. 37. import FileReader from "lib/file-reader"; class PoetryReader { giveMeSomePoetry(): string { let poem = FileReader.read("poem.txt"); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return poem; } } Same 
 problem 38
  38. 38. Hexagonal Architecture
  39. 39. The simplest way to 40 Separate Domain 
 & Infrastructure
  40. 40. 41 Infra. Domain
  41. 41. 42 Infra. Domain dependency
  42. 42. 43 Infra. Domain dependency
  43. 43. 44 Interface Adapter use implement
  44. 44. 45 Port Adapter
  45. 45. 46 Port Adapter Ports & Adapters architecture
  46. 46. 47 Business language
 only
  47. 47. 48 Left-side Right-side
  48. 48. 49 Left-side Right-side Adapter Adapter
  49. 49. 50 Left-side Right-side Adapter Adapter Adapter Adapter
  50. 50. A concrete example
  51. 51. class PoetryReader { } Domain 52
  52. 52. class PoetryReader { poetryLibrary: ObtainPoems; constructor(poetryLibrary!?: ObtainPoems) { this.poetryLibrary = poetryLibrary; } } Domain 53
  53. 53. class PoetryReader { poetryLibrary: ObtainPoems; constructor(poetryLibrary!?: ObtainPoems) { this.poetryLibrary = poetryLibrary; } } Domain 54 Dependency Injection
  54. 54. class PoetryReader { poetryLibrary: ObtainPoems; constructor(poetryLibrary!?: ObtainPoems) { this.poetryLibrary = poetryLibrary; } giveMeSomePoetry(): Poem { if (!this.poetryLibrary) { return "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return this.poetryLibrary.getAPoem(); } } Domain 55
  55. 55. class PoetryReader { poetryLibrary: ObtainPoems; constructor(poetryLibrary!?: ObtainPoems) { this.poetryLibrary = poetryLibrary; } giveMeSomePoetry(): Poem { if (!this.poetryLibrary) { return "If you could read a leaf or treernyou’d have no need of books. rn!-- © Alistair Cockburn (1987)"; } return this.poetryLibrary.getAPoem(); } } Domain 56
  56. 56. giveMeSomePoetry(): Poem { if (!this.poetryLibrary) { return "If you could read a leaf or treernyou’d have no need of books.rn!-- © Alistair Cockburn (1987)"; } return this.poetryLibrary.getAPoem(); } 57 giveMeSomePoetry(): string { let poem = FileReader.read("poem.txt"); if (!poem) { poem = "If you could read a leaf or treernyou’d have no need of books.rn!-- © Alistair Cockburn (1987)"; } return poem; } Implementation Intention
  57. 57. Domain type Poem = string; interface ObtainPoems { getAPoem(): Poem } 58
  58. 58. import * as fs from "fs"; import * as path from "path"; import ObtainPoems from "!../domain/obtain-poems"; class ObtainPoemsFromFS implements ObtainPoems { getAPoem() { const pathToPoem = path.join(!__dirname, "poem.txt"); const poem = fs.readFileSync(pathToPoem, { encoding: "utf8" }); return poem; } } Infra. 59
  59. 59. !// 1. Instantiate the right-side adapter(s) !// 2. Instantiate the hexagon (domain) !// 3. Instantiate the left-side adapter(s) 60
  60. 60. import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs"; !// 1. Instantiate the right-side adapter(s) const poetryLibrary = new ObtainPoemsFromFS(); !// 2. Instantiate the hexagon (domain) !// 3. Instantiate the left-side adapter(s) 61
  61. 61. import PoetryReader from "./domain/poetry-reader"; import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs"; !// 1. Instantiate the right-side adapter(s) const poetryLibrary = new ObtainPoemsFromFS(); !// 2. Instantiate the hexagon (domain) const poetryReader = new PoetryReader(poetryLibrary); !// 3. Instantiate the left-side adapter(s) 62
  62. 62. import PoetryReader from "./domain/poetry-reader"; import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs"; import ConsoleApi from "./infrastructure/console-api"; !// 1. Instantiate the right-side adapter(s) const poetryLibrary = new ObtainPoemsFromFS(); !// 2. Instantiate the hexagon (domain) const poetryReader = new PoetryReader(poetryLibrary); !// 3. Instantiate the left-side adapter(s) const consoleApi = new ConsoleApi(poetryReader); 63
  63. 63. import PoetryReader from "./domain/poetry-reader"; import ObtainPoemsFromFS from "./infrastructure/obtain-poems-from-fs"; import ConsoleApi from "./infrastructure/console-api"; !// 1. Instantiate the right-side adapter(s) const poetryLibrary = new ObtainPoemsFromFS(); !// 2. Instantiate the hexagon (domain) const poetryReader = new PoetryReader(poetryLibrary); !// 3. Instantiate the left-side adapter(s) const consoleApi = new ConsoleApi(poetryReader); !// App logic is only using left-side adapter(s). console.log("Here is some poetry:n"); consoleApi.ask(); 64
  64. 64. const poetryReader = new PoetryReader(); 65
  65. 65. const poetryReader = new PoetryReader(poetryLibrary); 66
  66. 66. Pros and cons
  67. 67. This ain't new 68 "Program to an Interface" − OOP "Isolate side effects" − FP The Onion Architecture
  68. 68. Plug different adapters to the Domain 69
  69. 69. A good architect defers decisions 70
  70. 70. Start with something simple 71
  71. 71. But… it's only the first step! 72
  72. 72. 73 More layers
  73. 73. 74 Ubiquitous Language 
 Bounded Contexts
 Domain modelling
  74. 74. Separate Domain & Infrastructure 75 🙏
  75. 75. Twitter 
 GitHub 
 Medium }@nicoespeon Nicolas Carlo
  76. 76. @swcraftmontreal Software Crafters Montréal Every 1st Tuesday of the month.
  77. 77. Bonuses
  78. 78. test("give verses when asked for poetry", () !=> { const poetryReader = new PoetryReader(); const verses = poetryReader.giveMeSomePoetry(); expect(verses).toEqual( "If you could read a leaf or treernyou’d have no need of books.rn!-- © Alistair Cockburn (1987)" ); }); Tests
  79. 79. test("give verses from a PoetryLibrary", () !=> { const poetryLibrary: ObtainPoems = { getAPoem() { return "I want to sleep…rn!-- Masaoka Shiki (1867-1902)"; } }; const poetryReader = new PoetryReader(poetryLibrary); const verses = poetryReader.giveMeSomePoetry(); expect(verses).toEqual( "I want to sleep…rn!-- Masaoka Shiki (1867-1902)" ); }); Tests

×