SlideShare a Scribd company logo
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

More Related Content

What's hot

Clojure 1.1 And Beyond
Clojure 1.1 And BeyondClojure 1.1 And Beyond
Clojure 1.1 And Beyond
Mike 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 Swift
Giordano 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 181
Mahmoud Samir Fayed
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
HamletDRC
 
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
Mahmoud 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 202
Mahmoud Samir Fayed
 
Javascript Primer
Javascript PrimerJavascript Primer
Javascript Primer
Adam Hepton
 
JavaScript Primer
JavaScript PrimerJavaScript Primer
JavaScript Primer
Daniel Cousineau
 
Promise: async programming hero
Promise: async programming heroPromise: async programming hero
Promise: async programming hero
The Software House
 
JavaScript Patterns
JavaScript PatternsJavaScript Patterns
JavaScript Patterns
Giordano Scalzo
 
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
 
JavaScript Primer
JavaScript PrimerJavaScript Primer
JavaScript Primer
Daniel Cousineau
 
The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_function
timotheeg
 
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
 
Groovy
GroovyGroovy
Groovy
Zen Urban
 
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
Kevlin Henney
 
Functional C++
Functional C++Functional C++
Functional C++
Kevlin Henney
 
ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with Groovy
Ivá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 Camp
julien.ponge
 

What's hot (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 to 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 code
Mario Gleichmann
 
Ugly code
Ugly codeUgly code
Ugly code
Odd-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 Application
Joni
 
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
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 script
CodiLime
 
Node Anti-Patterns and Bad Practices
Node Anti-Patterns and Bad PracticesNode Anti-Patterns and Bad Practices
Node Anti-Patterns and Bad Practices
Pedro Teixeira
 
SWP - A Generic Language Parser
SWP - A Generic Language ParserSWP - A Generic Language Parser
SWP - A Generic Language Parser
kamaelian
 
Parsing with Perl6 Grammars
Parsing with Perl6 GrammarsParsing with Perl6 Grammars
Parsing with Perl6 Grammars
abrummett
 
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
 
Elegant objects
Elegant objectsElegant objects
Elegant objects
📈 Paige Watson
 
GoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDGoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDD
Bartłomiej Kiełbasa
 
mobl
moblmobl
Why Sifu?
Why Sifu?Why Sifu?
Why Sifu?
Sifu
 
Why Sifu
Why SifuWhy Sifu
Why Sifu
LambdaWorks
 
Scala @ TomTom
Scala @ TomTomScala @ TomTom
Scala @ TomTom
Eric Bowman
 
Elm: give it a try
Elm: give it a tryElm: give it a try
Elm: give it a try
Eugene Zharkov
 
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
Kevin Hazzard
 
Intel JIT Talk
Intel JIT TalkIntel JIT Talk
Intel JIT Talk
iamdvander
 

Similar to 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 Sifu?Why Sifu?
Why Sifu?
 
Why Sifu
Why SifuWhy 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
 

Recently uploaded

Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom KittEnhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Peter Caitens
 
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptxMigration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
ervikas4
 
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and MoreManyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
narinav14
 
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdfBaha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid
 
ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.
Maitrey Patel
 
DECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSIS
DECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSISDECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSIS
DECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSIS
Tier1 app
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
Grant Fritchey
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
Marcin Chrost
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
DevOps Consulting Company | Hire DevOps Services
DevOps Consulting Company | Hire DevOps ServicesDevOps Consulting Company | Hire DevOps Services
DevOps Consulting Company | Hire DevOps Services
seospiralmantra
 
What’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete RoadmapWhat’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete Roadmap
Envertis Software Solutions
 
Orca: Nocode Graphical Editor for Container Orchestration
Orca: Nocode Graphical Editor for Container OrchestrationOrca: Nocode Graphical Editor for Container Orchestration
Orca: Nocode Graphical Editor for Container Orchestration
Pedro J. Molina
 
TMU毕业证书精仿办理
TMU毕业证书精仿办理TMU毕业证书精仿办理
TMU毕业证书精仿办理
aeeva
 
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
dakas1
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
Patrick Weigel
 
All you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVMAll you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVM
Alina Yurenko
 
Kubernetes at Scale: Going Multi-Cluster with Istio
Kubernetes at Scale:  Going Multi-Cluster  with IstioKubernetes at Scale:  Going Multi-Cluster  with Istio
Kubernetes at Scale: Going Multi-Cluster with Istio
Severalnines
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
gapen1
 
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptxOperational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
sandeepmenon62
 
ppt on the brain chip neuralink.pptx
ppt  on   the brain  chip neuralink.pptxppt  on   the brain  chip neuralink.pptx
ppt on the brain chip neuralink.pptx
Reetu63
 

Recently uploaded (20)

Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom KittEnhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
 
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptxMigration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
 
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and MoreManyata Tech Park Bangalore_ Infrastructure, Facilities and More
Manyata Tech Park Bangalore_ Infrastructure, Facilities and More
 
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdfBaha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
Baha Majid WCA4Z IBM Z Customer Council Boston June 2024.pdf
 
ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.
 
DECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSIS
DECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSISDECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSIS
DECODING JAVA THREAD DUMPS: MASTER THE ART OF ANALYSIS
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
 
DevOps Consulting Company | Hire DevOps Services
DevOps Consulting Company | Hire DevOps ServicesDevOps Consulting Company | Hire DevOps Services
DevOps Consulting Company | Hire DevOps Services
 
What’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete RoadmapWhat’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete Roadmap
 
Orca: Nocode Graphical Editor for Container Orchestration
Orca: Nocode Graphical Editor for Container OrchestrationOrca: Nocode Graphical Editor for Container Orchestration
Orca: Nocode Graphical Editor for Container Orchestration
 
TMU毕业证书精仿办理
TMU毕业证书精仿办理TMU毕业证书精仿办理
TMU毕业证书精仿办理
 
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
一比一原版(UMN毕业证)明尼苏达大学毕业证如何办理
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
 
All you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVMAll you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVM
 
Kubernetes at Scale: Going Multi-Cluster with Istio
Kubernetes at Scale:  Going Multi-Cluster  with IstioKubernetes at Scale:  Going Multi-Cluster  with Istio
Kubernetes at Scale: Going Multi-Cluster with Istio
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
 
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptxOperational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
 
ppt on the brain chip neuralink.pptx
ppt  on   the brain  chip neuralink.pptxppt  on   the brain  chip neuralink.pptx
ppt on the brain chip neuralink.pptx
 

Tactical DDD patterns in Go