Upgrade to PRO for Only $50/Yearโ€”Limited-Time Offer! ๐Ÿ”ฅ

DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO

DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN ENย GO

Avatar for Friends of Go

Friends of Go

October 06, 2019
Tweet

More Decks by Friends of Go

Other Decks in Programming

Transcript

  1. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO domingo, 6

    octubre de 2019 Joan Lรณpez de la Franca Beltran @joanjan14 Adriรกn Pรฉrez Gil @adrianpgl
  2. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO โ€œCOUNTERSโ€ -LA

    APLICACIร“N- โ€ฃ Registro e inicio de sesiรณn (usuarios) โ€ฃ Crear e incrementar (contadores) โ€ฃ Obtener estadรญsticas de uso (informes) โ€ฃ Sistema de widgets (widgets) โ€ฃ Sistema de facturaciรณn (facturaciรณn) โ€ฃ Y mรกsโ€ฆ
  3. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (ADRIรN) โ€ฃ Adriรกn Pรฉrez โ€ฃ a.k.a. @adrianpgl or @aperezg
  4. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (ADRIรN) โ€ฃ Adriรกn Pรฉrez โ€ฃ a.k.a. @adrianpgl or @aperezgโ€จ โ€จโ€จโ€จโ€จโ€จ โ€ฃ Desarrollador @ Europcar Mobility Group (Ubeeqo)
  5. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (ADRIรN) โ€ฃ Adriรกn Pรฉrez โ€ฃ a.k.a. @adrianpgl or @aperezgโ€จ โ€จโ€จโ€จโ€จโ€จ โ€ฃ Desarrollador @ Europcar Mobility Group (Ubeeqo)โ€จ โ€จโ€จโ€จโ€จโ€จโ€จโ€จ โ€ฃ Es su primera charla en una conferencia โ€ฃ Aunque ya ha dado varias formaciones
  6. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (JOAN) โ€ฃ Joan Lรณpez de la Franca โ€ฃ a.k.a. @joanjan14 or @joanlopez
  7. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (JOAN) โ€ฃ Joan Lรณpez de la Franca โ€ฃ a.k.a. @joanjan14 or @joanlopezโ€จ โ€จโ€จโ€จโ€จโ€จโ€จ โ€ฃ Desarrollador @ Cabify (Maxi Mobility)
  8. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (JOAN) โ€ฃ Joan Lรณpez de la Franca โ€ฃ a.k.a. @joanjan14 or @joanlopezโ€จ โ€จโ€จโ€จโ€จโ€จโ€จ โ€ฃ Desarrollador @ Cabify (Maxi Mobility)โ€จ โ€จโ€จโ€จโ€จโ€จโ€จ โ€ฃ Ponente jรบnior โ€ฃ @GopherConRu โ€ฃ @gopherconuk
  9. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ยฟQUIร‰NES SOMOS?

    (FOGO) โ€ฃ Nacimos a principios de aรฑo โ€ฃ Tenemos +500 seguidores โ€ฃ Hemos redactado +40 artรญculos en el blog โ€ฃ Impartimos formaciones a empresas โ€ฃ Grabamos un curso en CodelyTV โ€ฃ Killgrave: mock server -the easy way-
  10. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA โ€œNOโ€

    ARQUITECTURA / โ”œโ”€โ”€ counters.go โ”œโ”€โ”€ users.go โ”œโ”€โ”€ http.go โ”œโ”€โ”€ utils.go โ”œโ”€โ”€ main.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md
  11. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA โ€œNOโ€

    ARQUITECTURA / โ”œโ”€โ”€ counters.go โ”œโ”€โ”€ users.go โ”œโ”€โ”€ http.go โ”œโ”€โ”€ utils.go โ”œโ”€โ”€ main.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md package main import ( "log" "net/http" ) func main() { http.HandleFunc("/counters/increment", incrementHandler) http.HandleFunc("/counters/create", createHandler) log.Fatal(http.ListenAndServe(":8080", nil)) }
  12. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA โ€œNOโ€

    ARQUITECTURA / โ”œโ”€โ”€ counters.go โ”œโ”€โ”€ users.go โ”œโ”€โ”€ http.go โ”œโ”€โ”€ utils.go โ”œโ”€โ”€ main.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md package main import ( "net/http" ) func createHandler(w http.ResponseWriter, r *http.Request) { counterID, err := getKeyFromHttpRequest(r, "counter") if err != nil { w.WriteHeader(http.StatusInternalServerError) return } createCounter(counterID) w.WriteHeader(http.StatusOK) } func incrementHandler(w http.ResponseWriter, r *http.Request) { counterID, err := getKeyFromHttpRequest(r, "counter") if err != nil { w.WriteHeader(http.StatusInternalServerError) return } incrementCounter(counterID) }
  13. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA โ€œNOโ€

    ARQUITECTURA / โ”œโ”€โ”€ counters.go โ”œโ”€โ”€ users.go โ”œโ”€โ”€ http.go โ”œโ”€โ”€ utils.go โ”œโ”€โ”€ main.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md package main var counters = make(map[string]int) func createCounter(ID string) { counters[ID] = 0 } func incrementCounter(ID string) { counters[ID]++ }
  14. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA โ€œNOโ€

    ARQUITECTURA / โ”œโ”€โ”€ counters.go โ”œโ”€โ”€ users.go โ”œโ”€โ”€ http.go โ”œโ”€โ”€ utils.go โ”œโ”€โ”€ main.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md package main import ( "errors" "fmt" "log" "net/http" ) func getKeyFromHttpRequest(r *http.Request, key string) (string, error) { keys, ok := r.URL.Query()[key] if !ok || len(keys[0]) < 1 { log.Println("Url Param 'key' is missing") return "", errors.New(fmt.Sprintf("%s param is missing", key)) } return keys[0], nil }
  15. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA โ€œNOโ€

    ARQUITECTURA (CONTRAPRESTACIONES) โ€ฃ Paquete รบnico (WTF!?) โ€ฃ Pรฉrdida de visibilidad de variables โ€ฃ Difรญcil de testear โ€ฃ Difรญcil de ejecutar โ€ฃ go run main.go http.go counters.go users.go utils.go โ€ฃ go run *.go โ€ฃ go build -o counters && ./counters
  16. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA ARQUITECTURA

    DE PAQUETES / โ”œโ”€โ”€ counters โ”‚ โ”œโ”€โ”€ counters.go โ”œโ”€โ”€ users โ”‚ โ”œโ”€โ”€ users.go โ”œโ”€โ”€ http โ”‚ โ”œโ”€โ”€ handlers.go โ”‚ โ”œโ”€โ”€ constants.go โ”œโ”€โ”€ utils โ”‚ โ”œโ”€โ”€ http.go โ”œโ”€โ”€ main.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md
  17. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LA ARQUITECTURA

    DE PAQUETES (CONTRAPRESTACIONES) โ€ฃ Variables globales (= estado global) โ€ฃ Control de concurrencia complejo (nivel paquete) โ€ฃ Cรณdigo altamente acoplado โ€ฃ Difรญcil de testear โ€ฃ Complejo de extender
  18. โ€“ ROBERT C. MARTIN, CLEAN CODE โ€œSo if you want

    to go fast, if you want to get done quickly, if you want your code to be easy to write, make it easy to readโ€ DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO
  19. VALE, PERO ยฟCร“MO PUEDO ESTRUCTURAR MI APLICACIร“N GO SIGUIENDO LA

    ARQUITECTURA HEXAGONAL? DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO
  20. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ARQUITECTURA HEXAGONAL

    INFRASTRUCTURE APPLICATION DOMAIN / โ”œโ”€โ”€ cmd โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ pkg (o internal) โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md
  21. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ARQUITECTURA HEXAGONAL

    INFRASTRUCTURE APPLICATION DOMAIN / โ”œโ”€โ”€ cmd โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ pkg (o internal) โ”‚ โ”œโ”€โ”€ application/ โ”‚ โ”œโ”€โ”€ domain/ โ”‚ โ”œโ”€โ”€ infrastructure/ โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md
  22. โ€“ DAVE CHENEY, PRACTICAL GO โ€œName your package for what

    it provides, not what it contains.โ€ DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO
  23. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ARQUITECTURA HEXAGONAL

    / โ”œโ”€โ”€ cmd โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ pkg (o internal) โ”‚ โ”œโ”€โ”€ application/ โ”‚ โ”œโ”€โ”€ domain/ โ”‚ โ”œโ”€โ”€ infrastructure/ โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md
  24. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ARQUITECTURA HEXAGONAL

    / โ”œโ”€โ”€ cmd โ”‚ โ”œโ”€โ”€ counters-api โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ pkg (o internal) โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md INFRASTRUCTURE APPLICATION DOMAIN
  25. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ARQUITECTURA HEXAGONAL

    / โ”œโ”€โ”€ cmd โ”‚ โ”œโ”€โ”€ counters-api โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”‚ โ”œโ”€โ”€ report.go โ”œโ”€โ”€ docker-compose.yml โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md * test files were obviated for the sake of diagramโ€™s simplicity INFRASTRUCTURE APPLICATION DOMAIN
  26. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO INFRASTRUCTURA: SERVER

    / โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”œโ”€โ”€ http โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ requests.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ responses.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server.go โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package http import โ€ฆ func NewServer( fetchService fetching.Service, createService creating.Service, incrementService incrementing.Service, ) (http.Handler, error) { r := gin.New() r.Use(gin.Logger(), gin.Recovery()) // Auth (JWT) handler initialization authMiddleware, _ := jwt.NewGinMiddleware(fetchService) r.POST("/users/register", createUserHandlerBuilder(createService)) r.POST("/users/login", authMiddleware.LoginHandler) auth := r.Group("") auth.Use(authMiddleware.MiddlewareFunc()) auth.GET("/users/:userID", getUserHandlerBuilder(fetchService)) auth.POST("/counters", createCounterHandlerBuilder(createService)) auth.GET("/counters/:counterID", getCounterHandlerBuilder(fetchService)) auth.POST("/counters/increment", incrementCounterHandlerBuilder(fetchService, incrementService)) return r, nil }
  27. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO INFRASTRUCTURA: SERVER

    / โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”œโ”€โ”€ http โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ requests.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ responses.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server.go โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package http type CreateCounterRequest struct { Name string `json:"name"` } type IncrementCounterRequest struct { ID string `json:"id"` } type CreateUserRequest struct { Name string `json:"name"` Email string `json:"email"` Password string `json:"password"` } package http type CreateCounterResponse struct { ID string `json:"id"` Name string `json:"name"` Value uint `json:"value"` } type GetCounterResponse struct { ID string `json:"id"` Name string `json:"name"` Value uint `json:"value"` } type RegisterUserResponse struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` } type GetUserResponse struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` }
  28. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO INFRASTRUCTURA: SERVER

    / โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”œโ”€โ”€ http โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ requests.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ responses.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server.go โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ func createCounterHandlerBuilder(createService creating.Service) func(c *gin.Context) { return func(c *gin.Context) { var req CreateCounterRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } authorizedUserData, _ := c.Get(jwt.IdentityKey) authorizedUser := authorizedUserData.(jwt.User) counter, err := createService.CreateCounter(req.Name, authorizedUser.ID) if err != nil { if errors.IsWrongInput(err) { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } c.JSON(http.StatusOK, CreateCounterResponse{ ID: counter.ID, Name: counter.Name, Value: counter.Value, }) } }
  29. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO REPOSITORIO /

    โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package counters import โ€ฆ type Counter struct { ID string Name string Value uint LastUpdate time.Time BelongsTo string } type CounterRepository interface { Get(ID string) (*Counter, error) Save(counter Counter) error }
  30. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO REPOSITORIO /

    โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”‚ โ”œโ”€โ”€ inmemory โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ counters.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ users.go โ”‚ โ”‚ โ”œโ”€โ”€ mysql โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package inmemory import โ€ฆ type countersRepository struct { counters map[string]counters.Counter } var ( countersOnce sync.Once countersInstance *countersRepository ) func NewCountersRepository() counters.CounterRepository { countersOnce.Do(func() { countersInstance = &countersRepository{ counters: make(map[string]counters.Counter), } }) return countersInstance } func (r *countersRepository) Get(ID string) (*counters.Counter, error) { counter, ok := r.counters[ID] if !ok { return nil, errors.NewNotFound("counter with id %s not found", ID) } return &counter, nil } func (r *countersRepository) Save(counter counters.Counter) error { r.counters[counter.ID] = counter return nil }
  31. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO SERVICIO /

    โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”‚ โ”œโ”€โ”€ service.go โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package creating import โ€ฆ type Service interface { CreateCounter(name, belongsTo string) (counters.Counter, error) CreateUser(name, email, password string) (counters.User, error) } type service struct { counters counters.CounterRepository users counters.UserRepository } func NewCreateService(cR counters.CounterRepository, uR counters.UserRepository) Service { return &service{counters: cR, users: uR} } func (s *service) CreateCounter(name, belongsTo string) (counters.Counter, error) { // code here } func (s *service) CreateUser(name, email, password string) (counters.User, error) { // code here }
  32. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ENTIDADES /

    โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package counters import โ€ฆ const ( minNameLength = 3 defaultCounterValue = 0 ) type Counter struct { ID string Name string Value uint LastUpdate time.Time BelongsTo string } func NewCounter(name, belongsTo string) (*Counter, error) { if len(name) < minNameLength { return nil, errors.NewWrongInput("counter name %s is too short", name) } return &Counter{ ID: ulid.New(), Name: name, Value: defaultCounterValue, LastUpdate: time.Now(), BelongsTo: belongsTo, }, nil } func (c *Counter) Increment() { c.Value++ }
  33. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ENTIDADES /

    โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ package counters import โ€ฆ const ( minNameLength = 3 defaultCounterValue = 0 ) type Counter struct { ID string Name string Value uint LastUpdate time.Time BelongsTo string } func NewCounter(name, belongsTo string) (*Counter, error) { if len(name) < minNameLength { return nil, errors.NewWrongInput("counter name %s is too short", name) } return &Counter{ ID: ulid.New(), Name: name, Value: defaultCounterValue, LastUpdate: time.Now(), BelongsTo: belongsTo, }, nil } func (c *Counter) Increment() { c.Value++ }
  34. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO VALUE OBJECT

    / โ”œโ”€โ”€ cmd โ”œโ”€โ”€ pkg (or internal) โ”‚ โ”œโ”€โ”€ server โ”‚ โ”œโ”€โ”€ creating โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”œโ”€โ”€ storage โ”‚ โ”œโ”€โ”€ errors โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”œโ”€โ”€ user.go โ”œโ”€โ”€ โ€ฆ type Counter struct { ID *CounterID Name string Value uint LastUpdate time.Time BelongsTo string } func NewCounter(id *CounterID, name, belongsTo string) (*Counter, error) { // code here } type CounterID struct { id string } func NewCounterID() *CounterID { return &CounterID{ulid.New()} } func (c CounterID) Value() string { return c.id } func (c CounterID) String() string { return c.Value() }
  35. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO INCONVENIENTES DE

    ESTA ARQUITECTURA โ€ฃ El cรณdigo relacionado con el dominio puede crecer mucho y volverseโ€จ โ€จโ€จinmanejable. โ€ฃ Varias representaciones de dominio pueden producir representaciones muy โ€จ โ€จโ€จconfusas. โ€ฃ Los servicios de aplicaciรณn no tienen unos lรญmites muy definidos.
  36. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO LOS CONTEXTOS

    DE NUESTRA APLICACIร“N COUNTERS BC OWNER M COUNTER M USERS BC COUNTER M USER M
  37. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO CONTEXTUALIZANDO NUESTRA

    APLICACIร“N / โ”œโ”€ counters โ”‚ โ”œโ”€โ”€ โ€ฆ โ”œโ”€ users โ”‚ โ”œโ”€โ”€ โ€ฆ โ”œโ”€ kit โ”‚ โ”œโ”€โ”€ โ€ฆ โ”œโ”€โ”€ go.mod โ”œโ”€โ”€ go.sum โ”œโ”€โ”€ โ€ฆ * los ficheros de test han sido obviados para simplificar el diagrama
  38. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO CONTEXTUALIZANDO NUESTRA

    APLICACIร“N / โ”œโ”€โ”€ counters โ”‚ โ”œโ”€โ”€ cmd โ”‚ โ”‚ โ”œโ”€โ”€ counters-api โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”‚ โ”œโ”€โ”€ owners โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ kit * los ficheros de test han sido obviados para simplificar el diagrama / โ”œโ”€โ”€ users โ”‚ โ”œโ”€โ”€ cmd โ”‚ โ”‚ โ”œโ”€โ”€ users-api โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”œโ”€โ”€ users โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ kit โ”œโ”€โ”€ kit โ”œโ”€โ”€ go.mod โ”œโ”€โ”€ go.sum โ”œโ”€โ”€ โ€ฆ
  39. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO CONTEXTUALIZANDO NUESTRA

    APLICACIร“N / โ”œโ”€โ”€ counters โ”‚ โ”œโ”€โ”€ cmd โ”‚ โ”‚ โ”œโ”€โ”€ counters-api โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”‚ โ”œโ”€โ”€ owners โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ kit * los ficheros de test han sido obviados para simplificar el diagrama / โ”œโ”€โ”€ users โ”‚ โ”œโ”€โ”€ cmd โ”‚ โ”‚ โ”œโ”€โ”€ users-api โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”œโ”€โ”€ users โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ kit โ”œโ”€โ”€ kit โ”œโ”€โ”€ go.mod โ”œโ”€โ”€ go.sum โ”œโ”€โ”€ โ€ฆ
  40. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO CONTEXTUALIZANDO NUESTRA

    APLICACIร“N / โ”œโ”€โ”€ counters โ”‚ โ”œโ”€โ”€ cmd โ”‚ โ”‚ โ”œโ”€โ”€ counters-api โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ fetching โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ incrementing โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ counter.go โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”‚ โ”œโ”€โ”€ owners โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ kit * los ficheros de test han sido obviados para simplificar el diagrama / โ”œโ”€โ”€ users โ”‚ โ”œโ”€โ”€ cmd โ”‚ โ”‚ โ”œโ”€โ”€ users-api โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ server โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ main.go โ”‚ โ”œโ”€โ”€ internal โ”‚ โ”‚ โ”œโ”€โ”€ users โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”‚ โ”œโ”€โ”€ counters โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ โ€ฆ โ”‚ โ”œโ”€โ”€ kit โ”œโ”€โ”€ kit โ”œโ”€โ”€ go.mod โ”œโ”€โ”€ go.sum โ”œโ”€โ”€ โ€ฆ
  41. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO โ€ฃ Representa

    el concepto โ€œShared Kernelโ€ de DDD โ€ฃ Lo podemos aplicar tanto: โ€ฃ Entre mรณdulos โ€ฃ Entre bounded contexts โ€ฃ Seguimos nombrando a los paquetes por lo โ€จ โ€จโ€จโ€จque proveen, no lo que contienen: โ€ฃ utils, common, etc โ€ฃ strings, ulid, etc KIT
  42. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO PRร“XIMOS PASOS

    โ€ฃ Seguir a los maravillosos chicos de Friends of Go โ€ฃ (Si aรบn no habรฉis empezado con Go): โ€ฃ A Tour of Go: https://tour.golang.org/ โ€ฃ Curso de Codely: https://pro.codely.tv/ โ€ฃ Colaborar con la comunidad: โ€ฃ https://github.com/trending/go โ€ฃ https://github.com/friendsofgo/killgrave
  43. DESDE EL CAOS AL DOMAIN-DRIVEN DESIGN EN GO ENLACES AL

    MATERIAL โ€ฃ https://bit.ly/bcn-crafters-fogo-talk โ€ฃ https://github.com/friendsofgo/go-architecture-examples