SlideShare una empresa de Scribd logo
1 de 169
Descargar para leer sin conexión
roblaszczak
TACTICAL DDD
PATTERNS IN GOLANG
roblaszczak
Hello!
roblaszczak
Hello!
● Co-founder @Tree Dots Labs
roblaszczak
Hello!
● Co-founder @Tree Dots Labs
● Golang, Python, PHP
roblaszczak
TACTICAL DDD
PATTERNS IN GOLANG
roblaszczak
6
+ DDD = ?
roblaszczak
7
roblaszczak
8
roblaszczak
9
roblaszczak
10
Dlaczego to nie działa tak jak
ustaliliśmy?
roblaszczak
11
Myśleliśmy że wiecie że to tak
będzie działać
roblaszczak
12
Czy ktoś w ogóle wie jak to
działa?
roblaszczak
13
Poczekaj, sprawdzę w tym
handlerze na 1k linii i w
triggerach DB
roblaszczak
Nie do końca to mieliśmy na
myśli mówiąc “klient kupił
produkt”...
14
roblaszczak
15
roblaszczak
16
roblaszczak
17
roblaszczak
18
+ DDD =
roblaszczak
19
DOKŁADNE
ODZWIERCIEDLENIE
LOGIKI BIZNESOWEJ
roblaszczak
20
KOD KTÓRY MOŻNA
POKAZAĆ EKSPERTOWI
DOMENOWEMU
roblaszczak
21
BEZPIECZNE
WPROWADZANIE
ZNACZĄCYCH ZMIAN W
LOGICE BIZNESOWEJ
roblaszczak
22
LEGENDARNY
SAMODOKUMENTUJĄCY
SIĘ KOD
roblaszczak
23
UPORZĄDKOWANIE
NAZEWNICTWA
roblaszczak
24
WERYFIKACJA LOGIKI
DOMENOWEJ PRZED
ROZPOCZĘCIEM
IMPLEMENTACJI
roblaszczak
25
LITE DDD =
roblaszczak
26
EVENT STORMING
roblaszczak
27
HOT SPOTS
roblaszczak
28
ENTITY
roblaszczak
29
Nurse administer standard flu
vaccine dose to adult patient.
roblaszczak
30
patient := patient.NewPatient()
patient.SetShotType(vaccine.Flu)
patient.SetDose(10)
patient.SetNurse(nurse)
roblaszczak
31
patient := patient.NewPatient()
patient.SetShotType(vaccine.Flu)
patient.SetDose(10)
patient.SetNurse(nurse)
roblaszczak
32
patient.GiveFluShot()
roblaszczak
33
patient.GiveFluShot()
roblaszczak
34
vaccine :=
vaccine.StandardAdultFluDose()
nurse.AdministerFluVaccine(
patient,
vaccine,
)
roblaszczak
35
Nurse administer standard flu
vaccine dose to adult patient.
roblaszczak
36
vaccine :=
vaccine.StandardAdultFluDose()
nurse.AdministerFluVaccine(
patient,
vaccine,
)
roblaszczak
37
roblaszczak
38
roblaszczak
39
package backlog
type ItemID struct{ id.ID }
type Item struct {
id ItemID
name string
// ...
}
roblaszczak
40
package backlog
type ItemID struct{ id.ID }
type Item struct {
id ItemID
name string
// ...
}
roblaszczak
41
type ItemID struct{ id.ID }
roblaszczak
42
package id
type ID struct {
u ulid.ULID
valid bool
}
func New() ID {
return ID{ulid.MustNew(ulid.Timestamp(time.Now()), rand.Reader), true}
}
func FromString(s string) (ID, error) {}
func (i ID) String() string {}
func (i ID) Bytes() []byte {}
func (i ID) Equals(toCompare ID) bool {}
func (i ID) Empty() bool {}
roblaszczak
43
type ItemID id.ID
roblaszczak
44
type ItemID id.ID
roblaszczak
45
type ItemID struct{ id.ID }
roblaszczak
46
func NewItem(id ItemID, name string) (*Item, error) {
if id.Empty() {
return nil, ErrItemEmptyID
}
i := &Item{id: id}
if err := i.ChangeName(name); err != nil {
return nil, err
}
return i, nil
}
roblaszczak
47
func (i *Item) ChangeName(name string) error
{
if name == "" {
return ErrItemEmptyName
}
i.name = name
return nil
}
roblaszczak
48
var (
ErrItemEmptyName =
domain.NewIllegalStateError("name cannot be empty")
)
roblaszczak
49
func (i *Item) changeName(name string)
error {
if name == "" {
return ErrItemEmptyName
}
i.name = name
return nil
}
roblaszczak
50
func NewItem(id ItemID, name string) (*Item, error) {
if id.Empty() {
return nil, ErrItemEmptyID
}
i := &Item{id: id}
if err := i.ChangeName(name); err != nil {
return nil, err
}
return i, nil
}
roblaszczak
51
{
"valid_item",
args{backlog.ItemID{id.New()}, "foo"},
nil,
}, {
"missing_id",
args{backlog.ItemID{}, "foo"},
backlog.ErrItemEmptyID,
}, {
"missing_name",
args{backlog.ItemID{id.New()}, ""},
backlog.ErrItemEmptyName,
roblaszczak
52
{
"valid_item",
args{backlog.ItemID{id.New()}, "foo"},
nil,
}, {
"missing_id",
args{backlog.ItemID{}, "foo"},
backlog.ErrItemEmptyID,
}, {
"missing_name",
args{backlog.ItemID{id.New()}, ""},
backlog.ErrItemEmptyName,
roblaszczak
53
func TestNewItem(t *testing.T) {
type args struct {
id backlog.ItemID
name string
}
tests := []struct {
name string
args args
wantErr error
}{
roblaszczak
54
t.Run(tt.name, func(t *testing.T) {
_, err := backlog.NewItem(
tt.args.id, tt.args.name
)
assert.Equal(t, tt.wantErr, err)
})
roblaszczak
55
Lifetip: CTRL + Shift + T
roblaszczak
56
package backlog_test
roblaszczak
57
tests := []struct {
name string
args args
wantErr error
}{
roblaszczak
58
func TestItem_ChangeName(t *testing.T) {
roblaszczak
59
roblaszczak
60
{
name: "scheduled_for_release",
item: createTestScheduledForReleaseItem(t),
wantErr: nil,
},
roblaszczak
61
roblaszczak
62
{
name: "scheduled_for_release",
item: createTestScheduledForReleaseItem(t),
wantErr: nil,
},
roblaszczak
63
{
name: "not_scheduled_for_release",
item: createTestItem(t),
wantErr: backlog.ErrMustBeScheduledToCommit,
},
roblaszczak
64
{
name: "already_commited_to_sprint",
item: createTestCommitedToSprintItem(t),
wantErr: nil,
},
roblaszczak
65
func createTestItem(t *testing.T) *backlog.Item {
i, err := backlog.NewItem(
backlog.ItemID{id.New()},
"Foo",
)
require.NoError(t, err)
return i
}
roblaszczak
66
func createTestItem(t *testing.T) *backlog.Item {
i, err := backlog.NewItem(
backlog.ItemID{id.New()},
"Foo",
)
require.NoError(t, err)
return i
}
roblaszczak
67
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
roblaszczak
68
func createTestScheduledForReleaseItem(t *testing.T)
*backlog.Item {
i := createTestItem(t)
err := i.ScheduleRelease(time.Now())
require.NoError(t, err)
return i
}
roblaszczak
69
func createTestCommitedToSprintItem(t *testing.T)
*backlog.Item {
i := createTestScheduledForReleaseItem(t)
s := createTestSprint(t)
err := i.CommitToSprint(s)
require.NoError(t, err)
return i
}
roblaszczak
70
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := createTestSprint(t)
initialSprintID := tt.i.CommitedSprintID()
initialStatus := tt.i.Status()
err := tt.i.CommitToSprint(s)
assert.Equal(t, tt.wantErr, err)
// ...
})
}
roblaszczak
71
if tt.wantErr == nil {
assert.Equal(t, s.ID(), tt.i.CommitedSprintID())
assert.NotEqual(t, s.ID(), initialSprintID)
assert.Equal(
t, tt.i.Status(),
backlog.ItemStatusCommited
)
} else {
assert.Equal(t,initialSprintID,tt.i.CommitedSprintID())
assert.Equal(t, tt.item.Status(), initialStatus)
}
roblaszczak
72
if tt.wantErr == nil {
assert.Equal(t, s.ID(), tt.i.CommitedSprintID())
assert.NotEqual(t, s.ID(), initialSprintID)
assert.Equal(
t, tt.i.Status(),
backlog.ItemStatusCommited
)
} else {
assert.Equal(t,initialSprintID,tt.i.CommitedSprintID())
assert.Equal(t, tt.item.Status(), initialStatus)
}
roblaszczak
73
if tt.wantErr == nil {
assert.Equal(t, s.ID(), tt.i.CommitedSprintID())
assert.NotEqual(t, s.ID(), initialSprintID)
assert.Equal(
t, tt.i.Status(),
backlog.ItemStatusCommited
)
} else {
assert.Equal(t,initialSprintID,tt.i.CommitedSprintID())
assert.Equal(t, tt.item.Status(), initialStatus)
}
roblaszczak
74
if tt.wantErr == nil {
assert.Equal(t, s.ID(), tt.i.CommitedSprintID())
assert.NotEqual(t, s.ID(), initialSprintID)
assert.Equal(
t, tt.i.Status(),
backlog.ItemStatusCommited
)
} else {
assert.Equal(t,initialSprintID,tt.i.CommitedSprintID())
assert.Equal(t, tt.item.Status(), initialStatus)
}
roblaszczak
75
func (i *Item) CommitToSprint(s *sprint.Sprint) error {
if !i.IsScheduledForRelease() {
return ErrMustBeScheduledToCommit
}
if i.IsCommittedToSprint() {
i.UncommitFromSprint()
}
i.setStatus(ItemStatusCommited)
i.setSprintID(s.ID())
return nil
}
roblaszczak
76
func (i *Item) CommitToSprint(s *sprint.Sprint) error {
if !i.IsScheduledForRelease() {
return ErrMustBeScheduledToCommit
}
if i.IsCommittedToSprint() {
i.UncommitFromSprint()
}
i.setStatus(ItemStatusCommited)
i.setSprintID(s.ID())
return nil
}
roblaszczak
77
func (i *Item) CommitToSprint(s *sprint.Sprint) error {
if !i.IsScheduledForRelease() {
return ErrMustBeScheduledToCommit
}
if i.IsCommittedToSprint() {
i.UncommitFromSprint()
}
i.setStatus(ItemStatusCommited)
i.setSprintID(s.ID())
return nil
}
roblaszczak
78
func (i *Item) CommitToSprint(s *sprint.Sprint) error {
if !i.IsScheduledForRelease() {
return ErrMustBeScheduledToCommit
}
if i.IsCommittedToSprint() {
i.UncommitFromSprint()
}
i.setStatus(ItemStatusCommited)
i.setSprintID(s.ID())
return nil
}
roblaszczak
79
roblaszczak
80
func (i *Item) CommitToSprint(s *sprint.Sprint) error {
if !i.IsScheduledForRelease() {
return ErrMustBeScheduledToCommit
}
if i.IsCommittedToSprint() {
i.UncommitFromSprint()
}
i.setStatus(ItemStatusCommited)
i.setSprintID(s.ID())
return nil
}
roblaszczak
81
VALUE
OBJECT
roblaszczak
82
(RACZEJ)
NIE POSIADA ID
roblaszczak
83
JEST IMMUTABLE
roblaszczak
84
NIE POSIADA ZACHOWAŃ
roblaszczak
85
CZĘSTO ŁĄCZY 2
WARTOŚCI KTÓRE
ODDZIELNIE NIE MAJĄ
SENSU
roblaszczak
86
type Price struct {
cents int
currency string
}
roblaszczak
87
func NewPrice(cents int, currency string) (Price, error)
{
if cents <= 0 {
return Price{}, ErrCentsTooLow
}
if len(currency) != 3 {
return Price{}, ErrInvalidCurrency
}
return Price{cents, currency}, nil
}
roblaszczak
88
func NewPrice(cents int, currency string) (Price, error)
{
if cents <= 0 {
return Price{}, ErrCentsTooLow
}
if len(currency) != 3 {
return Price{}, ErrInvalidCurrency
}
return Price{cents, currency}, nil
}
roblaszczak
89
func NewPrice(cents int, currency string) (Price, error)
{
if cents <= 0 {
return Price{}, ErrCentsTooLow
}
if len(currency) != 3 {
return Price{}, ErrInvalidCurrency
}
return Price{cents, currency}, nil
}
roblaszczak
90
Price{}
roblaszczak
91
func (p Price) Empty() bool {
return p.cents == 0 || p.currency == ""
}
roblaszczak
92
func (p Price) Add(toAdd Price) (Price, error) {
if p.currency != toAdd.currency {
return Price{}, ErrCurrencyDoesntMatch
}
return NewPrice(p.cents+toAdd.cents, p.currency)
}
roblaszczak
93
REPOSITORY
roblaszczak
94
roblaszczak
95
roblaszczak
96
ODKŁADAMY DECYZJĘ
O BAZIE DANYCH
NA PÓŹNIEJ
roblaszczak
97
package backlog
type ItemRepository interface {
Add(*Item) error
ByID(ItemID) (*Item, error)
Update(*Item) error
}
roblaszczak
98
package backlog
// ….
var (
ErrItemNotFound = errors.New("item not found")
ErrItemAlreadyExists = errors.New("item already
exists")
)
roblaszczak
99
package backlog
type ItemRepository interface {
Add(*Item) error
ByID(ItemID) (*Item, error)
Update(*Item) error
}
roblaszczak
100
type BacklogItemRepository struct {
items map[string]backlog.Item
}
func (r *BacklogItemRepository) Add(i *backlog.Item)
error {
if _, ok := r.items[i.ID().String()]; ok {
return backlog.ErrItemAlreadyExists
}
r.items[i.ID().String()] = *i
return nil
}
roblaszczak
101
type BacklogItemRepository struct {
items map[string]backlog.Item
}
func (r *BacklogItemRepository) Add(i *backlog.Item)
error {
if _, ok := r.items[i.ID().String()]; ok {
return backlog.ErrItemAlreadyExists
}
r.items[i.ID().String()] = *i
return nil
}
roblaszczak
102
type BacklogItemRepository struct {
items map[string]backlog.Item
}
func (r *BacklogItemRepository) Add(i *backlog.Item)
error {
if _, ok := r.items[i.ID().String()]; ok {
return backlog.ErrItemAlreadyExists
}
r.items[i.ID().String()] = *i
return nil
}
roblaszczak
103
Jak tego teraz użyć?
roblaszczak
104
CQRS
roblaszczak
105
MAŁE SERWISY
APLIKACYJNE
PER USE CASE
roblaszczak
106
REUŻYWALNE
roblaszczak
107
roblaszczak
108
type CommitBacklogItemToSprint struct {
BacklogItemID backlog.ItemID
SprintID sprint.ID
}
roblaszczak
109
type CommitBacklogItemToSprint struct {
BacklogItemID backlog.ItemID
SprintID sprint.ID
}
roblaszczak
110
type CommitBacklogItemToSprintHandler struct {
sprintRepository sprint.Repository
backlogItemRepository backlog.ItemRepository
}
roblaszczak
111
func NewCommitToSprintHandler(
sprintRepository sprint.Repository,
backlogItemRepository backlog.ItemRepository,
) CommitBacklogItemToSprintHandler {
return CommitBacklogItemToSprintHandler{
sprintRepository,
backlogItemRepository,
}
}
roblaszczak
112
func NewCommitToSprintHandler(
sprintRepository sprint.Repository,
backlogItemRepository backlog.ItemRepository,
) CommitBacklogItemToSprintHandler {
return CommitBacklogItemToSprintHandler{
sprintRepository,
backlogItemRepository,
}
}
roblaszczak
113
type CommitBacklogItemToSprintHandler struct {
sprintRepository sprint.Repository
backlogItemRepository backlog.ItemRepository
}
roblaszczak
114
type CommitBacklogItemToSprintHandler struct {
sprintRepository sprint.Repository
backlogItemRepository backlog.ItemRepository
}
roblaszczak
115
type AddBacklogItem struct {
ID backlog.ItemID
Name string
}
func (a AddBacklogItemHandler) Handle(cmd AddBacklogItem)
error {
i, err := backlog.NewItem(cmd.ID, cmd.Name)
if err != nil {
return err
}
return a.backlogItemRepository.Add(i)
}
roblaszczak
116
type AddBacklogItem struct {
ID backlog.ItemID
Name string
}
func (a AddBacklogItemHandler) Handle(cmd AddBacklogItem)
error {
i, err := backlog.NewItem(cmd.ID, cmd.Name)
if err != nil {
return err
}
return a.backlogItemRepository.Add(i)
}
roblaszczak
117
type AddBacklogItem struct {
ID backlog.ItemID
Name string
}
func (a AddBacklogItemHandler) Handle(cmd AddBacklogItem)
error {
i, err := backlog.NewItem(cmd.ID, cmd.Name)
if err != nil {
return err
}
return a.backlogItemRepository.Add(i)
}
roblaszczak
118
type AddBacklogItem struct {
ID backlog.ItemID
Name string
}
func (a AddBacklogItemHandler) Handle(cmd AddBacklogItem)
error {
i, err := backlog.NewItem(cmd.ID, cmd.Name)
if err != nil {
return err
}
return a.backlogItemRepository.Add(i)
}
roblaszczak
119
type AddSprint struct {
// ...
roblaszczak
120
type CommitBacklogItemToSprint struct {
BacklogItemID backlog.ItemID
SprintID sprint.ID
}
roblaszczak
121
func (c CommitBacklogItemToSprintHandler) Handle(cmd
CommitBacklogItemToSprint) error {
sprint, err := c.sprintRepository.ByID(cmd.SprintID)
if err != nil {
return err
}
item, err :=
c.backlogItemRepository.ByID(cmd.BacklogItemID)
if err != nil {
return err
}
// ...
roblaszczak
122
func (c CommitBacklogItemToSprintHandler) Handle(cmd
CommitBacklogItemToSprint) error {
sprint, err := c.sprintRepository.ByID(cmd.SprintID)
if err != nil {
return err
}
item, err :=
c.backlogItemRepository.ByID(cmd.BacklogItemID)
if err != nil {
return err
}
// ...
roblaszczak
123
func (c CommitBacklogItemToSprintHandler) Handle(cmd
CommitBacklogItemToSprint) error {
// ...
if err := item.CommitToSprint(sprint); err != nil {
return err
}
return c.backlogItemRepository.Update(item)
}
roblaszczak
124
func (c CommitBacklogItemToSprintHandler) Handle(cmd
CommitBacklogItemToSprint) error {
// …
if item.IsCommitedToBacklog() {
return err
}
// …
return c.backlogItemRepository.Update(item)
}
roblaszczak
125
func (c CommitBacklogItemToSprintHandler) Handle(cmd
CommitBacklogItemToSprint) error {
// …
if item.IsCommitedToBacklog() {
return err
}
// …
return c.backlogItemRepository.Update(item)
}
roblaszczak
126
DOBRE MIEJSCE NA
CROSS-CUTTING
CONCERNS
roblaszczak
127
REST
roblaszczak
128
r.Get("/sprint/{sprintID}/backlog/item/{itemID}", func(w
http.ResponseWriter, r *http.Request) {
backlogItemID := id.FromStringMust(chi.URLParam(r, "itemID"))
sprintID := id.FromStringMust(chi.URLParam(r, "sprintID"))
err := h.Handle(command.CommitBacklogItemToSprint{
BacklogItemID: backlog.ItemID{backlogItemID},
SprintID: sprint.ID{sprintID},
})
if err != nil {
log.Println("error:", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
roblaszczak
129
r.Get("/sprint/{sprintID}/backlog/item/{itemID}", func(w
http.ResponseWriter, r *http.Request) {
backlogItemID := id.FromStringMust(chi.URLParam(r, "itemID"))
sprintID := id.FromStringMust(chi.URLParam(r, "sprintID"))
err := h.Handle(command.CommitBacklogItemToSprint{
BacklogItemID: backlog.ItemID{backlogItemID},
SprintID: sprint.ID{sprintID},
})
if err != nil {
log.Println("error:", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
roblaszczak
130
r.Get("/sprint/{sprintID}/backlog/item/{itemID}", func(w
http.ResponseWriter, r *http.Request) {
backlogItemID := id.FromStringMust(chi.URLParam(r, "itemID"))
sprintID := id.FromStringMust(chi.URLParam(r, "sprintID"))
err := h.Handle(command.CommitBacklogItemToSprint{
BacklogItemID: backlog.ItemID{backlogItemID},
SprintID: sprint.ID{sprintID},
})
if err != nil {
log.Println("error:", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
roblaszczak
131
Gdzie to
teraz
upchnąć?
roblaszczak
132
Jeden package
==
tracimy
enkapsulację
roblaszczak
133
.
├── app
│ └── command
├── domain
│ ├── backlog
│ └── sprint
├── infrastructure
│ └── memory
└── interfaces
└── rest
roblaszczak
134
.
├── app
│ └── command
├── domain
│ ├── backlog
│ └── sprint
├── infrastructure
│ └── memory
└── interfaces
└── rest
roblaszczak
135
.
├── app
│ └── command
├── domain
│ ├── backlog
│ └── sprint
├── infrastructure
│ └── memory
└── interfaces
└── rest
roblaszczak
136
.
├── app
│ └── command
├── domain
│ ├── backlog
│ └── sprint
├── infrastructure
│ └── memory
└── interfaces
└── rest
roblaszczak
137
.
├── app
│ └── command
├── domain
│ ├── backlog
│ └── sprint
├── infrastructure
│ └── memory
└── interfaces
└── rest
roblaszczak
138
domain
├── backlog
│ ├── item.go
│ ├── item_status.go
│ ├── repository.go
│ └── unmarshal.go
├── error.go
└── sprint
├── repository.go
└── sprint.go
roblaszczak
139
roblaszczak
140
github.com/roblaszczak/go-cleanarch
roblaszczak
141
github.com/roblaszczak/go-cleanarch
(wszystkie linki podam pod koniec prezentacji)
roblaszczak
142
roblaszczak
143
roblaszczak
144
type Item struct {
id ItemID `json:"id"`
name string `json:"name"`
status ItemStatus `json:"status"`
releaseSchedule time.Time `json:"release_schedule"`
commitedSprintID sprint.ID `json:"commited_sprint_id"`
}
roblaszczak
145
Zmiana widoku
wymaga zmiany w
domenie
roblaszczak
146
Atrybuty są
prywatne więc się
nie zmarshalują :)
roblaszczak
147
type BacklogItemView struct {
ID string `json:"id"`
Name string `json:"name"`
}
func NewBacklogItemView(item *backlog.Item)
BacklogItemView {
return BacklogItemView{item.ID().String(), item.Name()}
}
roblaszczak
148
SOLID Motivational Posters by Derick Bailey, used under CC BY-SA 3.0 US
roblaszczak
149
PERSYSTENTNE
REPOZYTORIUM
roblaszczak
150
roblaszczak
151
roblaszczak
152
Jak unmarshalować
obiekt domenowy bez
zbytniej ingerencji?
roblaszczak
153type ItemUnmarshalData struct {
ID ItemID
Name string
Status ItemStatus
ReleaseSchedule time.Time
CommitedSprintID sprint.ID
}
func UnmarshalItem(ud ItemUnmarshalData) *Item {
return &Item{
ud.ID,
ud.Name,
ud.Status,
ud.ReleaseSchedule,
ud.CommitedSprintID,
}
}
roblaszczak
154type ItemUnmarshalData struct {
ID ItemID
Name string
Status ItemStatus
ReleaseSchedule time.Time
CommitedSprintID sprint.ID
}
func UnmarshalItem(ud ItemUnmarshalData) *Item {
return &Item{
ud.ID,
ud.Name,
ud.Status,
ud.ReleaseSchedule,
ud.CommitedSprintID,
}
}
roblaszczak
155
ANEMICZNY
MODEL
roblaszczak
156
type User struct {
id ID
email string
name string
address string
}
func (u *User) Id() ID { return u.id }
func (u *User) SetId(id ID) { u.id = id }
func (u *User) Email() string { return u.email }
func (u *User) SetEmail(email string) { u.email = email }
func (u *User) Name() string { return u.name }
func (u *User) SetName(name string) { u.name = name }
func (u *User) Address() string { return u.address }
func (u *User) SetAddress(address string) { u.address = address }
roblaszczak
157
SILVER BULLET
NIE ISTNIEJE
DDD TEGO NIE ZMIENI
roblaszczak
158
CRUD IS OK
roblaszczak
159
LITE DDD =
roblaszczak
160
What next?
roblaszczak
161
Wrzucę linki na Twitterze
roblaszczak
roblaszczak
162
roblaszczak
163
roblaszczak
164
roblaszczak
165
https://threedots.tech/
roblaszczak
166
roblaszczak
Three Dots Labs
hire us!
roblaszczak
github.com/ThreeDotsLabs/watermill/
roblaszczak
DZIĘKI!
roblaszczak
robert@threedotslabs.com

Más contenido relacionado

La actualidad más candente

Clojure 1.1 And Beyond
Clojure 1.1 And BeyondClojure 1.1 And Beyond
Clojure 1.1 And BeyondMike Fogus
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftGiordano Scalzo
 
The Ring programming language version 1.5.2 book - Part 78 of 181
The Ring programming language version 1.5.2 book - Part 78 of 181The Ring programming language version 1.5.2 book - Part 78 of 181
The Ring programming language version 1.5.2 book - Part 78 of 181Mahmoud Samir Fayed
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokusHamletDRC
 
The Ring programming language version 1.2 book - Part 79 of 84
The Ring programming language version 1.2 book - Part 79 of 84The Ring programming language version 1.2 book - Part 79 of 84
The Ring programming language version 1.2 book - Part 79 of 84Mahmoud Samir Fayed
 
The Ring programming language version 1.8 book - Part 91 of 202
The Ring programming language version 1.8 book - Part 91 of 202The Ring programming language version 1.8 book - Part 91 of 202
The Ring programming language version 1.8 book - Part 91 of 202Mahmoud Samir Fayed
 
Javascript Primer
Javascript PrimerJavascript Primer
Javascript PrimerAdam Hepton
 
Promise: async programming hero
Promise: async programming heroPromise: async programming hero
Promise: async programming heroThe Software House
 
Introduction to modern c++ principles(part 1)
Introduction to modern c++ principles(part 1)Introduction to modern c++ principles(part 1)
Introduction to modern c++ principles(part 1)Oky Firmansyah
 
The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_functiontimotheeg
 
Lisp Macros in 20 Minutes (Featuring Clojure)
Lisp Macros in 20 Minutes (Featuring Clojure)Lisp Macros in 20 Minutes (Featuring Clojure)
Lisp Macros in 20 Minutes (Featuring Clojure)Phil Calçado
 
What We Talk About When We Talk About Unit Testing
What We Talk About When We Talk About Unit TestingWhat We Talk About When We Talk About Unit Testing
What We Talk About When We Talk About Unit TestingKevlin Henney
 
ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyIván López Martín
 
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...OPITZ CONSULTING Deutschland
 
Java 7 JUG Summer Camp
Java 7 JUG Summer CampJava 7 JUG Summer Camp
Java 7 JUG Summer Campjulien.ponge
 

La actualidad más candente (20)

Clojure 1.1 And Beyond
Clojure 1.1 And BeyondClojure 1.1 And Beyond
Clojure 1.1 And Beyond
 
How to Clone Flappy Bird in Swift
How to Clone Flappy Bird in SwiftHow to Clone Flappy Bird in Swift
How to Clone Flappy Bird in Swift
 
The Ring programming language version 1.5.2 book - Part 78 of 181
The Ring programming language version 1.5.2 book - Part 78 of 181The Ring programming language version 1.5.2 book - Part 78 of 181
The Ring programming language version 1.5.2 book - Part 78 of 181
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
The Ring programming language version 1.2 book - Part 79 of 84
The Ring programming language version 1.2 book - Part 79 of 84The Ring programming language version 1.2 book - Part 79 of 84
The Ring programming language version 1.2 book - Part 79 of 84
 
The Ring programming language version 1.8 book - Part 91 of 202
The Ring programming language version 1.8 book - Part 91 of 202The Ring programming language version 1.8 book - Part 91 of 202
The Ring programming language version 1.8 book - Part 91 of 202
 
Javascript Primer
Javascript PrimerJavascript Primer
Javascript Primer
 
JavaScript Primer
JavaScript PrimerJavaScript Primer
JavaScript Primer
 
Promise: async programming hero
Promise: async programming heroPromise: async programming hero
Promise: async programming hero
 
JavaScript Patterns
JavaScript PatternsJavaScript Patterns
JavaScript Patterns
 
Introduction to modern c++ principles(part 1)
Introduction to modern c++ principles(part 1)Introduction to modern c++ principles(part 1)
Introduction to modern c++ principles(part 1)
 
JavaScript Primer
JavaScript PrimerJavaScript Primer
JavaScript Primer
 
The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_function
 
Lisp Macros in 20 Minutes (Featuring Clojure)
Lisp Macros in 20 Minutes (Featuring Clojure)Lisp Macros in 20 Minutes (Featuring Clojure)
Lisp Macros in 20 Minutes (Featuring Clojure)
 
Groovy
GroovyGroovy
Groovy
 
What We Talk About When We Talk About Unit Testing
What We Talk About When We Talk About Unit TestingWhat We Talk About When We Talk About Unit Testing
What We Talk About When We Talk About Unit Testing
 
Functional C++
Functional C++Functional C++
Functional C++
 
ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with Groovy
 
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
 
Java 7 JUG Summer Camp
Java 7 JUG Summer CampJava 7 JUG Summer Camp
Java 7 JUG Summer Camp
 

Similar a Tactical DDD patterns in Go

Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"LogeekNightUkraine
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)Anders Jönsson
 
Empathic Programming - How to write comprehensible code
Empathic Programming - How to write comprehensible codeEmpathic Programming - How to write comprehensible code
Empathic Programming - How to write comprehensible codeMario Gleichmann
 
Ugly code
Ugly codeUgly code
Ugly codeOdd-e
 
Tips and Tricks of Developing .NET Application
Tips and Tricks of Developing .NET ApplicationTips and Tricks of Developing .NET Application
Tips and Tricks of Developing .NET ApplicationJoni
 
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...DevGAMM Conference
 
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java scriptCodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java scriptCodiLime
 
Node Anti-Patterns and Bad Practices
Node Anti-Patterns and Bad PracticesNode Anti-Patterns and Bad Practices
Node Anti-Patterns and Bad PracticesPedro Teixeira
 
SWP - A Generic Language Parser
SWP - A Generic Language ParserSWP - A Generic Language Parser
SWP - A Generic Language Parserkamaelian
 
Parsing with Perl6 Grammars
Parsing with Perl6 GrammarsParsing with Perl6 Grammars
Parsing with Perl6 Grammarsabrummett
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Juan Pablo
 
GoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDGoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDBartłomiej Kiełbasa
 
Why Sifu?
Why Sifu?Why Sifu?
Why Sifu?Sifu
 
C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607Kevin Hazzard
 
Intel JIT Talk
Intel JIT TalkIntel JIT Talk
Intel JIT Talkiamdvander
 

Similar a Tactical DDD patterns in Go (20)

Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
 
Empathic Programming - How to write comprehensible code
Empathic Programming - How to write comprehensible codeEmpathic Programming - How to write comprehensible code
Empathic Programming - How to write comprehensible code
 
Ugly code
Ugly codeUgly code
Ugly code
 
Tips and Tricks of Developing .NET Application
Tips and Tricks of Developing .NET ApplicationTips and Tricks of Developing .NET Application
Tips and Tricks of Developing .NET Application
 
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
 
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java scriptCodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
 
Node Anti-Patterns and Bad Practices
Node Anti-Patterns and Bad PracticesNode Anti-Patterns and Bad Practices
Node Anti-Patterns and Bad Practices
 
SWP - A Generic Language Parser
SWP - A Generic Language ParserSWP - A Generic Language Parser
SWP - A Generic Language Parser
 
Parsing with Perl6 Grammars
Parsing with Perl6 GrammarsParsing with Perl6 Grammars
Parsing with Perl6 Grammars
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#
 
Elegant objects
Elegant objectsElegant objects
Elegant objects
 
GoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDGoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDD
 
mobl
moblmobl
mobl
 
Why Sifu
Why SifuWhy Sifu
Why Sifu
 
Why Sifu?
Why Sifu?Why Sifu?
Why Sifu?
 
Scala @ TomTom
Scala @ TomTomScala @ TomTom
Scala @ TomTom
 
Elm: give it a try
Elm: give it a tryElm: give it a try
Elm: give it a try
 
C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607C# 6 and 7 and Futures 20180607
C# 6 and 7 and Futures 20180607
 
Intel JIT Talk
Intel JIT TalkIntel JIT Talk
Intel JIT Talk
 

Último

Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsJean Silva
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesAmazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesKrzysztofKkol1
 
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingShane Coughlan
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencessuser9e7c64
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf31events.com
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Not a Kubernetes fan? The state of PaaS in 2024
Not a Kubernetes fan? The state of PaaS in 2024Not a Kubernetes fan? The state of PaaS in 2024
Not a Kubernetes fan? The state of PaaS in 2024Anthony Dahanne
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profileakrivarotava
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxRTS corp
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingShane Coughlan
 

Último (20)

Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero results
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesAmazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
 
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conference
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Not a Kubernetes fan? The state of PaaS in 2024
Not a Kubernetes fan? The state of PaaS in 2024Not a Kubernetes fan? The state of PaaS in 2024
Not a Kubernetes fan? The state of PaaS in 2024
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profile
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
 

Tactical DDD patterns in Go