{
  "slug": "Building-Modular-Product-Management-in-Go-with-SQLC--Uber-FX--and-Gin-bfb6dfaada6b",
  "title": "🧱 Building Modular Product Management in Go with SQLC, Uber FX, and Gin",
  "subtitle": "In a recent commit to go-simple, a robust and idiomatic product management module that exemplifies clean architecture, modularity, and…",
  "excerpt": "In a recent commit to go-simple, a robust and idiomatic product management module that exemplifies clean architecture, modularity, and…",
  "date": "2025-09-28",
  "tags": [
    "Go",
    "Architecture"
  ],
  "readingTime": "3 min",
  "url": "https://medium.com/@mobinshaterian/building-modular-product-management-in-go-with-sqlc-uber-fx-and-gin-bfb6dfaada6b",
  "hero": "https://cdn-images-1.medium.com/max/800/1*oiFm9Fz7MHT9clBFIdtByA.png",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "🧱 Building Modular Product Management in Go with SQLC, Uber FX, and Gin"
    },
    {
      "type": "paragraph",
      "html": "In a recent commit to go-simple, a robust and idiomatic product management module that exemplifies clean architecture, modularity, and lifecycle-aware service design in Go. This article walks through the key decisions and implementation patterns that shaped the commit, and reflects on the architectural philosophy behind it."
    },
    {
      "type": "paragraph",
      "html": "In the previous session, we created a database. In this session, we plan to develop both a controller and a service for managing products, which will extend the functionality of the application."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*oiFm9Fz7MHT9clBFIdtByA.png",
      "alt": "🧱 Building Modular Product Management in Go with SQLC, Uber FX, and Gin",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧩 Domain-Centric Service Layer"
    },
    {
      "type": "paragraph",
      "html": "At the heart of the module is a <code>product.Service</code> that wraps SQLC-generated queries. Rather than exposing raw database logic, Mobin designed a clean API that accepts DTOs and returns domain-specific responses:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "func (s *Service) Create(ctx context.Context, req dto.AdminCreateProductRequest)\n(dto.ProductResponse, error\n)\nfunc (s *Service) Update(ctx context.Context, req dto.AdminUpdateProductRequest)\n(dto.ProductResponse, error\n)\nfunc (s *Service) Delete(ctx context.Context, id int32) errorfunc (s *Service) GetProductByID(ctx\ncontext.Context, id int32) (dto.ProductResponse, error\n)\nfunc (s *Service) ListProducts(ctx context.Context) ([]dto.ProductResponse, error)"
    },
    {
      "type": "paragraph",
      "html": "This approach encapsulates SQLC’s <code>CreateProduct</code>, <code>UpdateProduct</code>, and <code>ListProducts</code> methods, while translating them into clean, predictable DTOs. It also avoids unnecessary interfaces, favoring direct composition for simplicity and performance."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🌐 Gin-Based Admin Controller"
    },
    {
      "type": "paragraph",
      "html": "To expose the service via HTTP, Mobin implemented a Gin controller with full CRUD support:"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "<code>POST /admin/products</code> → Create product",
        "<code>PUT /admin/products/:id</code> → Update product",
        "<code>DELETE /admin/products/:id</code> → Delete product",
        "<code>GET /admin/products/:id</code> → Get product by ID",
        "<code>GET /admin/products</code> → List all products"
      ]
    },
    {
      "type": "paragraph",
      "html": "Each handler uses <code>ShouldBindJSON</code>, parses path parameters, and delegates to the service layer. Errors are handled via a centralized <code>response.JSONError</code> helper, which logs structured JSON using <code>zap</code>:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "func JSONError(ctx *gin.Context, status int, err error, logger *zap.Logger) {\n    logger.Error(\"request error\", zap.Int(\"status\", status), zap.String(\"error\", err.Error()),\n    zap.String(\"path\", ctx.Request.URL.Path), zap.String(\"method\", ctx.Request.Method),\n    )\n    ctx.AbortWithStatusJSON(status, ErrorResponse{\n        Error: err.Error()\n    })\n}"
    },
    {
      "type": "paragraph",
      "html": "This ensures consistent error responses and observability across the API."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧠 SQLC Integration with DBTX"
    },
    {
      "type": "paragraph",
      "html": "The service layer relies on SQLC’s <code>Queries</code> struct, which expects a <code>DBTX</code> interface. Mobin resolved this via Uber FX by providing a <code>*sql.DB</code> instance:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "fx.Provide(func(cfg *config.Config) (*sql.DB, error) {    return sql.Open(\"postgres\",\ncfg.DatabaseURL)})"
    },
    {
      "type": "paragraph",
      "html": "This satisfies the <code>DBTX</code> dependency and allows SQLC to operate seamlessly within the FX lifecycle."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "📚 Swagger Documentation"
    },
    {
      "type": "paragraph",
      "html": "Mobin also added Swagger annotations to document the API. For example, the <code>GetProductByID</code> endpoint includes:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "// @Summary Get a product by ID// @Description Retrieve a single product using its unique ID// @Tags\nProducts// @Param id path int true \"Product ID\"// @Success 200 {object} dto.ProductResponse//\n@Failure 400 {object} response.ErrorResponse// @Failure 500 {object} response.ErrorResponse//\n@Router /products/{id} [get]"
    },
    {
      "type": "paragraph",
      "html": "To ensure Swagger parses the DTOs correctly, Mobin added dummy references and imported the <code>dto</code> package explicitly. Fields in <code>ProductResponse</code> were exported and tagged with <code>json</code> and <code>example</code> annotations for clarity."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧭 Architectural Philosophy"
    },
    {
      "type": "paragraph",
      "html": "This commit reflects Mobin’s evolving architectural mindset:"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "<strong>Domain-centric modularity</strong>: Each feature lives in its own folder with DTOs, service, and controller.",
        "<strong>Lifecycle-aware design</strong>: Uber FX wires dependencies cleanly and supports migration hooks.",
        "<strong>Idiomatic Go</strong>: Avoids unnecessary interfaces, favors composition, and uses clear naming.",
        "<strong>Observability</strong>: Structured logging with <code>zap</code> and consistent error handling.",
        "<strong>Scalability</strong>: Ready for client/admin separation, Swagger docs, and test harnesses."
      ]
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧩 Conclusion"
    },
    {
      "type": "paragraph",
      "html": "Mobin’s work on the <code>go-simple</code> project showcases a thoughtful blend of pragmatism and architectural clarity. By grounding the product module in SQLC, Uber FX, and Gin, he’s built a foundation that’s not only idiomatic but also scalable, testable, and maintainable. The decision to avoid unnecessary interfaces, embrace domain-centric DTOs, and centralize error handling with structured logging reflects a deep understanding of Go’s strengths and the realities of production-grade development."
    },
    {
      "type": "paragraph",
      "html": "This commit isn’t just about adding a feature — it’s about codifying a philosophy: that reliability, clarity, and modularity are not trade-offs, but pillars of sustainable software. Whether you’re building a microservice or a monolith, Mobin’s approach offers a blueprint for clean, composable Go systems that evolve gracefully with your team and your codebase."
    },
    {
      "type": "paragraph",
      "html": "Let me know if you’d like to turn this into a publishable blog post or internal architecture doc — it’s a great example of how thoughtful design leads to resilient systems."
    }
  ]
}
