Pattern CQRS & Couches (Server)
1. Pattern CQRS simplifié
Section titled “1. Pattern CQRS simplifié”Angular 22 ──HTTP/WS/SSE──> Gin handler ──> Command/Query handler │ │ Command ─────┘ └──── Query │ │ GORM write Redis GET ──miss──> GORM read │ │ │ invalidate cache <───────────────┴──── Redis SET ◄──┘ │ gRPC agent / événement temps réelInterfaces du module application
Section titled “Interfaces du module application”package application
import ( "context"
"github.com/google/uuid" "github.com/your-org/berth-api/internal/domain")
type Repository interface { FindByID(ctx context.Context, id uuid.UUID) (*domain.Application, error) FindAll(ctx context.Context) ([]domain.Application, error) Create(ctx context.Context, app *domain.Application) error Save(ctx context.Context, app *domain.Application) error Delete(ctx context.Context, id uuid.UUID) error}
type AgentGateway interface { RestartContainer(ctx context.Context, agentID uuid.UUID, containerID string) error}Command handler complet : redémarrer une application
Section titled “Command handler complet : redémarrer une application”package command
import ( "context" "fmt"
"github.com/google/uuid" "github.com/your-org/berth-api/internal/modules/application")
type RestartApplication struct { ApplicationID uuid.UUID}
type RestartApplicationHandler struct { repo application.Repository agent application.AgentGateway cache application.Cache}
func NewRestartApplicationHandler(repo application.Repository, agent application.AgentGateway, cache application.Cache) *RestartApplicationHandler { return &RestartApplicationHandler{repo: repo, agent: agent, cache: cache}}
func (h *RestartApplicationHandler) Handle(ctx context.Context, cmd RestartApplication) error { app, err := h.repo.FindByID(ctx, cmd.ApplicationID) if err != nil { return err } if app.AgentID == uuid.Nil || app.ContainerID == "" { return fmt.Errorf("application %s: agent ou conteneur manquant", app.ID) } if err := h.agent.RestartContainer(ctx, app.AgentID, app.ContainerID); err != nil { return fmt.Errorf("restart container: %w", err) } app.Status = "restarting" if err := h.repo.Save(ctx, app); err != nil { return err } return h.cache.InvalidateApplication(ctx, app.ID)}Query handler complet : obtenir une application
Section titled “Query handler complet : obtenir une application”package query
import ( "context"
"github.com/google/uuid" "github.com/your-org/berth-api/internal/domain" "github.com/your-org/berth-api/internal/modules/application")
type GetApplicationHandler struct { repo application.Repository cache application.Cache}
func NewGetApplicationHandler(repo application.Repository, cache application.Cache) *GetApplicationHandler { return &GetApplicationHandler{repo: repo, cache: cache}}
func (h *GetApplicationHandler) Handle(ctx context.Context, id uuid.UUID) (*domain.Application, error) { if cached, err := h.cache.GetApplication(ctx, id); err == nil && cached != nil { return cached, nil } app, err := h.repo.FindByID(ctx, id) if err != nil { return nil, err } _ = h.cache.SetApplication(ctx, app) return app, nil}Handler Gin
Section titled “Handler Gin”package application
import ( "net/http"
"github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/your-org/berth-api/internal/modules/application/command" "github.com/your-org/berth-api/internal/modules/application/query" "github.com/your-org/berth-api/internal/pkg/response")
type Handler struct { restart *command.RestartApplicationHandler get *query.GetApplicationHandler}
func (h *Handler) Get(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { response.Error(c, http.StatusBadRequest, "invalid_id", "Identifiant invalide"); return } app, err := h.get.Handle(c.Request.Context(), id) if err != nil { response.FromError(c, err); return } response.JSON(c, http.StatusOK, app)}
func (h *Handler) Restart(c *gin.Context) { id, err := uuid.Parse(c.Param("id")) if err != nil { response.Error(c, http.StatusBadRequest, "invalid_id", "Identifiant invalide"); return } if err := h.restart.Handle(c.Request.Context(), command.RestartApplication{ApplicationID: id}); err != nil { response.FromError(c, err); return } response.JSON(c, http.StatusAccepted, gin.H{"status": "restarting", "id": id})}