فهرست منبع

CQRS y REPOSITORY PATTERN

cesarssh.dev@gmail.com 1 سال پیش
کامیت
1d53096252

+ 29 - 0
Dockerfile

@@ -0,0 +1,29 @@
+# Build Stage
+FROM golang:1.23 AS builder
+
+# Establece el directorio de trabajo en el contenedor
+WORKDIR /app
+
+# Copia solo los archivos necesarios para la instalación de dependencias
+COPY go.mod go.sum ./ 
+
+# Instala las dependencias
+RUN go mod download
+
+# Copia el resto de los archivos de la aplicación
+COPY . .
+
+# Compila la aplicación, apuntando a tu archivo main.go dentro de la carpeta cmd
+RUN go build -o /app/cqr-api-go ./cmd/main.go
+
+# Final Stage: Usa una imagen base más ligera
+FROM alpine:latest
+
+# Establece el directorio de trabajo
+WORKDIR /app
+
+# Copia el binario construido desde la etapa de construcción
+COPY --from=builder /app/cqr-api-go /app/cqr-api-go
+
+# Comando para ejecutar la aplicación
+CMD ["./cqr-api-go"]

+ 39 - 0
cmd/main.go

@@ -0,0 +1,39 @@
+package main
+
+import (
+	"log"
+	"net/http"
+
+	"github.com/CesarSSH/cqrs-api-go/db"
+	"github.com/CesarSSH/cqrs-api-go/internal/handlers"
+	"github.com/CesarSSH/cqrs-api-go/internal/models"
+	"github.com/CesarSSH/cqrs-api-go/internal/repository"
+	"github.com/CesarSSH/cqrs-api-go/internal/service"
+	"github.com/gorilla/mux"
+	"github.com/CesarSSH/cqrs-api-go/routes"
+
+)
+
+func main() {
+	//Conexion de la DB
+	db.DBConnection()
+
+	// Migrar el modelo
+	db.AutoMigrate(&models.User{})
+
+	// Inicializar repositorios y servicios
+	userRepo := repository.NewUserRepository(db.DB)
+
+	userCmdService := service.NewUserCommandService(userRepo)
+	userQryService := service.NewUserQueryService(userRepo)
+	userHandler := handlers.NewUserHandler(userCmdService, userQryService)
+
+	// Configurar el enrutador
+	r := mux.NewRouter()
+	// Registrar las rutas de usuarios
+    routes.SetupUserRoutes(r, userHandler)
+
+	// Iniciar el servidor
+	log.Println("API corriendo en :8080")
+	log.Fatal(http.ListenAndServe(":8080", r))
+}

+ 32 - 0
db/connection.go

@@ -0,0 +1,32 @@
+package db
+
+import (
+	"log"
+
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+)
+
+// Config to connect mySQL
+var DSN = "root:password@tcp(127.0.0.1:3306)/mydbusers?charset=utf8mb4&parseTime=True&loc=Local"
+
+var DB *gorm.DB
+
+func DBConnection() {
+	var error error
+	DB, error = gorm.Open(mysql.Open(DSN), &gorm.Config{})
+
+	if error != nil {
+		log.Fatal(error)
+	} else {
+		log.Println("DB connected")
+	}
+}
+
+// AutoMigrate realiza la migración de los modelos
+func AutoMigrate(models ...interface{}) {
+	err := DB.AutoMigrate(models...)
+	if err != nil {
+		log.Fatal("Error al realizar la migration:", err)
+	}
+}

+ 51 - 0
docker-compose.yml

@@ -0,0 +1,51 @@
+version: '3.7'
+
+services:
+  app:
+    container_name: go-api
+    build:
+      context: .
+      dockerfile: Dockerfile
+    image: go-api
+    ports:
+      - 8080:8080
+    environment:
+      - DB_HOST=mysql
+      - DB_PORT=3306
+      - DB_USER=root
+      - DB_PASSWORD=password
+      - DB_NAME=mydbusers
+    depends_on:
+      mysql:
+        condition: service_healthy
+    networks:
+      - go-network
+
+  mysql:
+    container_name: go-mysql
+    image: mysql:latest
+    environment:
+      - MYSQL_ROOT_PASSWORD=password
+      - MYSQL_DATABASE=mydbusers
+
+    volumes:
+      - dbdata:/var/lib/mysql
+      - ./initdb:/docker-entrypoint-initdb.d
+    ports:
+      - "3306:3306"
+
+    networks:
+      - go-network
+    healthcheck:
+      test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
+      interval: 10s
+      timeout: 5s
+      retries: 3
+
+volumes:
+  dbdata:
+
+
+networks:
+  go-network:
+    driver: bridge

+ 14 - 0
go.mod

@@ -0,0 +1,14 @@
+module github.com/CesarSSH/cqrs-api-go
+
+go 1.23.1
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/go-sql-driver/mysql v1.8.1 // indirect
+	github.com/gorilla/mux v1.8.1 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	golang.org/x/text v0.19.0 // indirect
+	gorm.io/driver/mysql v1.5.7 // indirect
+	gorm.io/gorm v1.25.12 // indirect
+)

+ 18 - 0
go.sum

@@ -0,0 +1,18 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
+gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
+gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
+gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=

+ 11 - 0
info.txt

@@ -0,0 +1,11 @@
+#REPOS UTILIZADOS
+
+go get -u gorm.io/driver/mysql
+go get -u github.com/gorilla/mux
+
+#Comandos basicos
+docker-compose up --build
+docker-compose up -d
+docker-compose down
+docker exec -it go-mysql mysql -u root -p  //ENTRAR AQUI Y CREAR LA BASE DE DATOS CON EL NOMBRE estudiantes
+go run cmd/main.go

+ 1 - 0
initdb/create_db.sql

@@ -0,0 +1 @@
+CREATE DATABASE IF NOT EXISTS mydbusers;

+ 28 - 0
internal/command/create_user_command.go

@@ -0,0 +1,28 @@
+package command
+
+import "errors"
+
+type CreateUserCommand struct {
+	// FirstName string
+	// LastName  string
+	FirstName string `json:"first_name"`
+	LastName  string `json:"last_name"`
+}
+
+func NewCreateUserCommand(firstName, lastName string) *CreateUserCommand {
+	return &CreateUserCommand{
+		FirstName: firstName,
+		LastName:  lastName,
+	}
+}
+
+// Validate valida los campos del comando
+func (c *CreateUserCommand) Validate() error {
+	if c.FirstName == "" {
+		return errors.New("el nombre es requerido")
+	}
+	if c.LastName == "" {
+		return errors.New("el apellido es requerido")
+	}
+	return nil
+}

+ 55 - 0
internal/handlers/user_handler.go

@@ -0,0 +1,55 @@
+package handlers
+
+import (
+	"github.com/CesarSSH/cqrs-api-go/internal/command"
+	"github.com/CesarSSH/cqrs-api-go/internal/service"
+
+	"encoding/json"
+	"net/http"
+)
+
+type UserHandler struct {
+	commandService *service.UserCommandService
+	queryService   *service.UserQueryService
+}
+
+func NewUserHandler(cmdService *service.UserCommandService, qryService *service.UserQueryService) *UserHandler {
+	return &UserHandler{
+		commandService: cmdService,
+		queryService:   qryService,
+	}
+}
+
+func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
+	var cmd command.CreateUserCommand
+	if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	// Llamar a la validación
+	if err := cmd.Validate(); err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+
+	if err := h.commandService.CreateUser(&cmd); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	w.WriteHeader(http.StatusCreated)
+	// Enviar un mensaje de éxito como respuesta
+	response := map[string]string{"message": "Usuario creado"}
+	json.NewEncoder(w).Encode(response)
+}
+
+func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
+	users, err := h.queryService.GetUsers()
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	w.Header().Set("Content-Type", "application/json")
+	json.NewEncoder(w).Encode(users)
+}

+ 12 - 0
internal/models/user.go

@@ -0,0 +1,12 @@
+package models
+
+import (
+	"gorm.io/gorm"
+)
+
+type User struct {
+	gorm.Model
+	FirstName string `json:"first_name"`
+	LastName  string `json:"last_name"`
+}
+

+ 7 - 0
internal/query/get_users_query.go

@@ -0,0 +1,7 @@
+package query
+
+type GetUsersQuery struct {}
+
+func NewGetUsersQuery() *GetUsersQuery {
+    return &GetUsersQuery{}
+}

+ 32 - 0
internal/repository/user_repository.go

@@ -0,0 +1,32 @@
+package repository
+
+import (
+	"log"
+
+	"github.com/CesarSSH/cqrs-api-go/internal/models"
+	"gorm.io/gorm"
+)
+
+type UserRepository interface {
+	Create(user *models.User) error
+	GetAll() ([]models.User, error)
+}
+
+type userRepository struct {
+	db *gorm.DB
+}
+
+func NewUserRepository(db *gorm.DB) UserRepository {
+	return &userRepository{db}
+}
+
+func (r *userRepository) Create(user *models.User) error {
+	log.Printf("Creating user: %+v\n", user) // Agrega esta línea para depuración
+	return r.db.Create(user).Error
+}
+
+func (r *userRepository) GetAll() ([]models.User, error) {
+	var users []models.User
+	err := r.db.Find(&users).Error
+	return users, err
+}

+ 25 - 0
internal/service/user_command_service.go

@@ -0,0 +1,25 @@
+package service
+
+import (
+	"github.com/CesarSSH/cqrs-api-go/internal/command"
+	"github.com/CesarSSH/cqrs-api-go/internal/models"
+	"github.com/CesarSSH/cqrs-api-go/internal/repository"
+)
+
+type UserCommandService struct {
+	userRepository repository.UserRepository
+}
+
+func NewUserCommandService(repo repository.UserRepository) *UserCommandService {
+	return &UserCommandService{
+		userRepository: repo,
+	}
+}
+
+func (s *UserCommandService) CreateUser(cmd *command.CreateUserCommand) error {
+	user := &models.User{
+		FirstName: cmd.FirstName,
+		LastName:  cmd.LastName,
+	}
+	return s.userRepository.Create(user)
+}

+ 20 - 0
internal/service/user_query_service.go

@@ -0,0 +1,20 @@
+package service
+
+import (
+	"github.com/CesarSSH/cqrs-api-go/internal/models"
+	"github.com/CesarSSH/cqrs-api-go/internal/repository"
+)
+
+type UserQueryService struct {
+	userRepository repository.UserRepository
+}
+
+func NewUserQueryService(repo repository.UserRepository) *UserQueryService {
+	return &UserQueryService{
+		userRepository: repo,
+	}
+}
+
+func (s *UserQueryService) GetUsers() ([]models.User, error) {
+	return s.userRepository.GetAll()
+}

+ 12 - 0
routes/user_routes.go

@@ -0,0 +1,12 @@
+package routes
+
+import (
+	"github.com/CesarSSH/cqrs-api-go/internal/handlers"
+	"github.com/gorilla/mux"
+)
+
+// SetupUserRoutes configura las rutas relacionadas con los usuarios
+func SetupUserRoutes(r *mux.Router, userHandler *handlers.UserHandler) {
+	r.HandleFunc("/api/v1/users", userHandler.CreateUser).Methods("POST")
+	r.HandleFunc("/api/v1/users", userHandler.GetUsers).Methods("GET")
+}