<go_file>
<go_version> go1.23
<go_pkg> controllers

// controllers/shipmentController.go
// Pattern: struct controller + constructor injection + Swagger annotations
// Observed in: Medical-App-Core/controllers/
package controllers

import (
	"net/http"
	"time"

	"logistics-api/services/contracts"
	"github.com/gofiber/fiber/v2"
)

// ShipmentController handles HTTP requests for the Shipment resource.
type ShipmentController struct {
	ShipmentService contracts.ShipmentServiceContract
}

// NewShipmentController constructs a ShipmentController with its service dependency.
func NewShipmentController(svc contracts.ShipmentServiceContract) *ShipmentController {
	return &ShipmentController{ShipmentService: svc}
}

// CreateShipment godoc
// @Summary     Create a new shipment
// @Description Create a new shipment in the system
// @Tags        Shipments
// @Accept      json
// @Produce     json
// @Security    BearerAuth
// @Param       body body contracts.ShipmentInputDTO true "Shipment data"
// @Success     201  {object} fiber.Map
// @Failure     400  {object} fiber.Map
// @Failure     422  {object} fiber.Map
// @Failure     500  {object} fiber.Map
// @Router      /shipments [post]
func (ctrl *ShipmentController) CreateShipment(c *fiber.Ctx) error {
	var input contracts.ShipmentInputDTO
	if err := c.BodyParser(&input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid request payload"})
	}

	if input.OriginAddress == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "origin_address is required"})
	}
	if input.DestinationAddress == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "destination_address is required"})
	}

	id, err := ctrl.ShipmentService.CreateFromInput(input)
	if err != nil {
		return c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}

	return c.Status(http.StatusCreated).JSON(fiber.Map{
		"message": "Shipment created successfully",
		"id":      id,
	})
}

// GetByTrackingCode godoc
// @Summary     Get shipment by TrackingCode
// @Description Retrieve a single shipment identified by its TrackingCode
// @Tags        Shipments
// @Produce     json
// @Security    BearerAuth
// @Param       tracking_code path string true "Shipment TrackingCode"
// @Success     200 {object} contracts.ShipmentDTO
// @Failure     400 {object} fiber.Map
// @Failure     404 {object} fiber.Map
// @Router      /shipments/{tracking_code} [get]
func (ctrl *ShipmentController) GetByTrackingCode(c *fiber.Ctx) error {
	identifier := c.Params("tracking_code")
	if identifier == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "tracking_code is required"})
	}

	result, err := ctrl.ShipmentService.FindByTrackingCode(identifier)
	if err != nil {
		return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": err.Error()})
	}

	return c.Status(http.StatusOK).JSON(result)
}

// ListShipments godoc
// @Summary     List all shipments
// @Description Returns a paginated list of shipments
// @Tags        Shipments
// @Produce     json
// @Security    BearerAuth
// @Param       page  query int false "Page number (default 1)"
// @Param       limit query int false "Page size (default 20)"
// @Success     200  {array}  contracts.ShipmentDTO
// @Failure     500  {object} fiber.Map
// @Router      /shipments [get]
func (ctrl *ShipmentController) ListShipments(c *fiber.Ctx) error {
	page  := c.QueryInt("page", 1)
	limit := c.QueryInt("limit", 20)
	if page < 1  { page = 1 }
	if limit < 1 { limit = 20 }

	results, err := ctrl.ShipmentService.List(page, limit)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "failed to retrieve shipments"})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{
		"data":  results,
		"page":  page,
		"limit": limit,
	})
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> main

// cmd/main.go — Fiber router setup
// Pattern: group-based routing + auth middleware + Swagger protection
// Observed in: Medical-App-Core/cmd/main.go
package main

import (
	"log"
	"os"

	"logistics-api/controllers"
	"logistics-api/initializers"
	_ "logistics-api/docs"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/basicauth"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/swagger"
	"github.com/joho/godotenv"
)

// bearerAuthMiddleware validates the Authorization: Bearer <token> header.
// Returns 401 if missing or invalid; calls Next() on success.
func bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {
	return func(c *fiber.Ctx) error {
		header := c.Get("Authorization")
		if len(header) <= 7 || header[:7] != "Bearer " {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error": "missing or malformed Authorization header",
			})
		}
		if err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error":  "invalid or expired token",
				"reason": err.Error(),
			})
		}
		return c.Next()
	}
}

func configureMiddleware(app *fiber.App) {
	app.Use(cors.New(cors.Config{
		AllowOrigins: "*",
		AllowMethods: "GET,POST,PUT,PATCH,DELETE,OPTIONS",
		AllowHeaders: "Origin, Content-Type, Accept, Authorization",
	}))
	app.Use(logger.New(logger.Config{
		Format: `[$\{time\}] $\{status\} - $\{method\} $\{path\}\n`,
	}))
}

func registerRoutes(app *fiber.App, svc *initializers.Services) {
	auth := app.Group("/auth")
	auth.Post("/token",    controllers.NewAuthController(svc.AuthTokenService).GenerateToken)
	auth.Post("/validate", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)

	protected := app.Group("/", bearerAuthMiddleware(svc))

	entityCtrl := controllers.NewShipmentController(svc.ShipmentService)
	shipments := protected.Group("/shipments")
	shipments.Post("/",              entityCtrl.CreateShipment)
	shipments.Get("/",               entityCtrl.ListShipments)
	shipments.Get("/:tracking_code",   entityCtrl.GetByTrackingCode)
}

func main() {
	if err := godotenv.Load(); err != nil {
		log.Println("warning: .env file not found, using environment")
	}

	db  := initializers.InitialDB()
	initializers.RunMigrations(db)
	svc := initializers.InitServices(db)

	app := fiber.New(fiber.Config{AppName: "logistics-api v1"})
	configureMiddleware(app)

	// Swagger endpoint protected by basic auth
	swaggerUser := os.Getenv("SWAGGER_USER")
	swaggerPass := os.Getenv("SWAGGER_PASSWORD")
	if swaggerUser == "" || swaggerPass == "" {
		log.Fatal("SWAGGER_USER and SWAGGER_PASSWORD must be set")
	}
	swaggerAuth := basicauth.New(basicauth.Config{
		Users: map[string]string{swaggerUser: swaggerPass},
	})
	app.Get("/swagger/*", swaggerAuth, swagger.HandlerDefault)

	registerRoutes(app, svc)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("server listening on :%s", port)
	if err := app.Listen(":" + port); err != nil {
		log.Fatalf("server error: %v", err)
	}
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> controllers

// controllers/shipmentController.go
// Pattern: struct controller + constructor injection + Swagger annotations
// Observed in: Medical-App-Core/controllers/
package controllers

import (
	"net/http"
	"time"

	"logistics-api/services/contracts"
	"github.com/gofiber/fiber/v2"
)

// ShipmentController handles HTTP requests for the Shipment resource.
type ShipmentController struct {
	ShipmentService contracts.ShipmentServiceContract
}

// NewShipmentController constructs a ShipmentController with its service dependency.
func NewShipmentController(svc contracts.ShipmentServiceContract) *ShipmentController {
	return &ShipmentController{ShipmentService: svc}
}

// CreateShipment godoc
// @Summary     Create a new shipment
// @Description Create a new shipment in the system
// @Tags        Shipments
// @Accept      json
// @Produce     json
// @Security    BearerAuth
// @Param       body body contracts.ShipmentInputDTO true "Shipment data"
// @Success     201  {object} fiber.Map
// @Failure     400  {object} fiber.Map
// @Failure     422  {object} fiber.Map
// @Failure     500  {object} fiber.Map
// @Router      /shipments [post]
func (ctrl *ShipmentController) CreateShipment(c *fiber.Ctx) error {
	var input contracts.ShipmentInputDTO
	if err := c.BodyParser(&input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid request payload"})
	}

	if input.OriginAddress == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "origin_address is required"})
	}
	if input.DestinationAddress == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "destination_address is required"})
	}

	id, err := ctrl.ShipmentService.CreateFromInput(input)
	if err != nil {
		return c.Status(http.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()})
	}

	return c.Status(http.StatusCreated).JSON(fiber.Map{
		"message": "Shipment created successfully",
		"id":      id,
	})
}

// GetByTrackingCode godoc
// @Summary     Get shipment by TrackingCode
// @Description Retrieve a single shipment identified by its TrackingCode
// @Tags        Shipments
// @Produce     json
// @Security    BearerAuth
// @Param       tracking_code path string true "Shipment TrackingCode"
// @Success     200 {object} contracts.ShipmentDTO
// @Failure     400 {object} fiber.Map
// @Failure     404 {object} fiber.Map
// @Router      /shipments/{tracking_code} [get]
func (ctrl *ShipmentController) GetByTrackingCode(c *fiber.Ctx) error {
	identifier := c.Params("tracking_code")
	if identifier == "" {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "tracking_code is required"})
	}

	result, err := ctrl.ShipmentService.FindByTrackingCode(identifier)
	if err != nil {
		return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": err.Error()})
	}

	return c.Status(http.StatusOK).JSON(result)
}

// ListShipments godoc
// @Summary     List all shipments
// @Description Returns a paginated list of shipments
// @Tags        Shipments
// @Produce     json
// @Security    BearerAuth
// @Param       page  query int false "Page number (default 1)"
// @Param       limit query int false "Page size (default 20)"
// @Success     200  {array}  contracts.ShipmentDTO
// @Failure     500  {object} fiber.Map
// @Router      /shipments [get]
func (ctrl *ShipmentController) ListShipments(c *fiber.Ctx) error {
	page  := c.QueryInt("page", 1)
	limit := c.QueryInt("limit", 20)
	if page < 1  { page = 1 }
	if limit < 1 { limit = 20 }

	results, err := ctrl.ShipmentService.List(page, limit)
	if err != nil {
		return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "failed to retrieve shipments"})
	}

	return c.Status(http.StatusOK).JSON(fiber.Map{
		"data":  results,
		"page":  page,
		"limit": limit,
	})
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> main

// cmd/main.go — Fiber router setup
// Pattern: group-based routing + auth middleware + Swagger protection
// Observed in: Medical-App-Core/cmd/main.go
package main

import (
	"log"
	"os"

	"logistics-api/controllers"
	"logistics-api/initializers"
	_ "logistics-api/docs"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/basicauth"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/swagger"
	"github.com/joho/godotenv"
)

// bearerAuthMiddleware validates the Authorization: Bearer <token> header.
// Returns 401 if missing or invalid; calls Next() on success.
func bearerAuthMiddleware(svc *initializers.Services) fiber.Handler {
	return func(c *fiber.Ctx) error {
		header := c.Get("Authorization")
		if len(header) <= 7 || header[:7] != "Bearer " {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error": "missing or malformed Authorization header",
			})
		}
		if err := svc.AuthTokenService.ValidateToken(header[7:]); err != nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"error":  "invalid or expired token",
				"reason": err.Error(),
			})
		}
		return c.Next()
	}
}

func configureMiddleware(app *fiber.App) {
	app.Use(cors.New(cors.Config{
		AllowOrigins: "*",
		AllowMethods: "GET,POST,PUT,PATCH,DELETE,OPTIONS",
		AllowHeaders: "Origin, Content-Type, Accept, Authorization",
	}))
	app.Use(logger.New(logger.Config{
		Format: `[$\{time\}] $\{status\} - $\{method\} $\{path\}\n`,
	}))
}

func registerRoutes(app *fiber.App, svc *initializers.Services) {
	auth := app.Group("/auth")
	auth.Post("/token",    controllers.NewAuthController(svc.AuthTokenService).GenerateToken)
	auth.Post("/validate", controllers.NewAuthController(svc.AuthTokenService).ValidateToken)

	protected := app.Group("/", bearerAuthMiddleware(svc))

	entityCtrl := controllers.NewShipmentController(svc.ShipmentService)
	shipments := protected.Group("/shipments")
	shipments.Post("/",              entityCtrl.CreateShipment)
	shipments.Get("/",               entityCtrl.ListShipments)
	shipments.Get("/:tracking_code",   entityCtrl.GetByTrackingCode)
}

func main() {
	if err := godotenv.Load(); err != nil {
		log.Println("warning: .env file not found, using environment")
	}

	db  := initializers.InitialDB()
	initializers.RunMigrations(db)
	svc := initializers.InitServices(db)

	app := fiber.New(fiber.Config{AppName: "logistics-api v1"})
	configureMiddleware(app)

	// Swagger endpoint protected by basic auth
	swaggerUser := os.Getenv("SWAGGER_USER")
	swaggerPass := os.Getenv("SWAGGER_PASSWORD")
	if swaggerUser == "" || swaggerPass == "" {
		log.Fatal("SWAGGER_USER and SWAGGER_PASSWORD must be set")
	}
	swaggerAuth := basicauth.New(basicauth.Config{
		Users: map[string]string{swaggerUser: swaggerPass},
	})
	app.Get("/swagger/*", swaggerAuth, swagger.HandlerDefault)

	registerRoutes(app, svc)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("server listening on :%s", port)
	if err := app.Listen(":" + port); err != nil {
		log.Fatalf("server error: %v", err)
	}
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> utils

// utils/responseUtil.go
// Pattern: response utility interface + JSON helper
// Observed in: Medical-App-Core/utils/responseUtil.go
package utils

import (
	"encoding/json"

	"github.com/gofiber/fiber/v2"
)

// ResponseUtil abstracts JSON response construction for testability.
type ResponseUtil interface {
	RespondWithJSON(c *fiber.Ctx, code int, payload any) error
	RespondWithError(c *fiber.Ctx, code int, message string) error
}

// JSONResponseUtil implements ResponseUtil using Fiber's context.
type JSONResponseUtil struct{}

func (ru *JSONResponseUtil) RespondWithJSON(c *fiber.Ctx, code int, payload any) error {
	body, err := json.Marshal(payload)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).
			JSON(fiber.Map{"error": "failed to marshal response"})
	}
	c.Set("Content-Type", "application/json")
	return c.Status(code).Send(body)
}

func (ru *JSONResponseUtil) RespondWithError(c *fiber.Ctx, code int, message string) error {
	return c.Status(code).JSON(fiber.Map{"error": message})
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> entities

// domain/entities/patient.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Patient represents the patients database table.
type Patient struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID      `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string         `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Contact        string         `gorm:"type:varchar(50);not null" json:"contact" validate:"required"`
	CPF            string         `gorm:"type:varchar(14);uniqueIndex" json:"cpf,omitempty"`
	SSN            string         `gorm:"type:varchar(11);uniqueIndex" json:"ssn,omitempty"`
	DOB            time.Time      `gorm:"type:date" json:"dob"`
	Gender         string         `gorm:"type:varchar(20)" json:"gender,omitempty"`
	Address        string         `gorm:"type:text" json:"address,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Patient) TableName() string { return "patients" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Patient) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// domain/repositories/contracts/patientRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// PatientRepository defines the persistence contract for Patient entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type PatientRepository interface {
	Create(dto dtos.PatientDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.PatientDTO, error)
	FindByCPF(cpf string) (*dtos.PatientDTO, error)
	List(page, limit int) ([]dtos.PatientDTO, error)
	Update(id uuid.UUID, dto dtos.PatientDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> repositories

// infra/repositories/patientRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// PatientRepositoryImpl implements contracts.PatientRepository using GORM.
type PatientRepositoryImpl struct {
	db *gorm.DB
}

// NewPatientRepository constructs a new PatientRepositoryImpl.
func NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {
	return &PatientRepositoryImpl{db: db}
}

func (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {
	entity := entities.Patient{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("patient create: %w", err)
	}
	return entity.ID, nil
}

func (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient not found: %s", id)
		}
		return nil, fmt.Errorf("patient find: %w", err)
	}
	dto := dtos.PatientDTO{} // map entity → DTO
	return &dto, nil
}

func (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.Where("cpf = ?", cpf).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient with cpf=%s not found", cpf)
		}
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	dto := dtos.PatientDTO{}
	return &dto, nil
}

func (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {
	var entities []entities.Patient
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("patient list: %w", err)
	}
	dtos := make([]dtos.PatientDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {
	result := r.db.Model(&entities.Patient{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("patient update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}

func (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Patient{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("patient delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> entities

// domain/entities/patient.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Patient represents the patients database table.
type Patient struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID      `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string         `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Contact        string         `gorm:"type:varchar(50);not null" json:"contact" validate:"required"`
	CPF            string         `gorm:"type:varchar(14);uniqueIndex" json:"cpf,omitempty"`
	SSN            string         `gorm:"type:varchar(11);uniqueIndex" json:"ssn,omitempty"`
	DOB            time.Time      `gorm:"type:date" json:"dob"`
	Gender         string         `gorm:"type:varchar(20)" json:"gender,omitempty"`
	Address        string         `gorm:"type:text" json:"address,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Patient) TableName() string { return "patients" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Patient) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// domain/repositories/contracts/patientRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// PatientRepository defines the persistence contract for Patient entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type PatientRepository interface {
	Create(dto dtos.PatientDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.PatientDTO, error)
	FindByCPF(cpf string) (*dtos.PatientDTO, error)
	List(page, limit int) ([]dtos.PatientDTO, error)
	Update(id uuid.UUID, dto dtos.PatientDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> repositories

// infra/repositories/patientRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// PatientRepositoryImpl implements contracts.PatientRepository using GORM.
type PatientRepositoryImpl struct {
	db *gorm.DB
}

// NewPatientRepository constructs a new PatientRepositoryImpl.
func NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {
	return &PatientRepositoryImpl{db: db}
}

func (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {
	entity := entities.Patient{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("patient create: %w", err)
	}
	return entity.ID, nil
}

func (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient not found: %s", id)
		}
		return nil, fmt.Errorf("patient find: %w", err)
	}
	dto := dtos.PatientDTO{} // map entity → DTO
	return &dto, nil
}

func (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.Where("cpf = ?", cpf).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient with cpf=%s not found", cpf)
		}
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	dto := dtos.PatientDTO{}
	return &dto, nil
}

func (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {
	var entities []entities.Patient
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("patient list: %w", err)
	}
	dtos := make([]dtos.PatientDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {
	result := r.db.Model(&entities.Patient{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("patient update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}

func (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Patient{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("patient delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> entities

// domain/entities/patient.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Patient represents the patients database table.
type Patient struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID      `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string         `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Contact        string         `gorm:"type:varchar(50);not null" json:"contact" validate:"required"`
	CPF            string         `gorm:"type:varchar(14);uniqueIndex" json:"cpf,omitempty"`
	SSN            string         `gorm:"type:varchar(11);uniqueIndex" json:"ssn,omitempty"`
	DOB            time.Time      `gorm:"type:date" json:"dob"`
	Gender         string         `gorm:"type:varchar(20)" json:"gender,omitempty"`
	Address        string         `gorm:"type:text" json:"address,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Patient) TableName() string { return "patients" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Patient) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// domain/repositories/contracts/patientRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// PatientRepository defines the persistence contract for Patient entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type PatientRepository interface {
	Create(dto dtos.PatientDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.PatientDTO, error)
	FindByCPF(cpf string) (*dtos.PatientDTO, error)
	List(page, limit int) ([]dtos.PatientDTO, error)
	Update(id uuid.UUID, dto dtos.PatientDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> repositories

// infra/repositories/patientRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// PatientRepositoryImpl implements contracts.PatientRepository using GORM.
type PatientRepositoryImpl struct {
	db *gorm.DB
}

// NewPatientRepository constructs a new PatientRepositoryImpl.
func NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {
	return &PatientRepositoryImpl{db: db}
}

func (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {
	entity := entities.Patient{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("patient create: %w", err)
	}
	return entity.ID, nil
}

func (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient not found: %s", id)
		}
		return nil, fmt.Errorf("patient find: %w", err)
	}
	dto := dtos.PatientDTO{} // map entity → DTO
	return &dto, nil
}

func (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.Where("cpf = ?", cpf).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient with cpf=%s not found", cpf)
		}
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	dto := dtos.PatientDTO{}
	return &dto, nil
}

func (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {
	var entities []entities.Patient
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("patient list: %w", err)
	}
	dtos := make([]dtos.PatientDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {
	result := r.db.Model(&entities.Patient{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("patient update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}

func (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Patient{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("patient delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> entities

// domain/entities/patient.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Patient represents the patients database table.
type Patient struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID      `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string         `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Contact        string         `gorm:"type:varchar(50);not null" json:"contact" validate:"required"`
	CPF            string         `gorm:"type:varchar(14);uniqueIndex" json:"cpf,omitempty"`
	SSN            string         `gorm:"type:varchar(11);uniqueIndex" json:"ssn,omitempty"`
	DOB            time.Time      `gorm:"type:date" json:"dob"`
	Gender         string         `gorm:"type:varchar(20)" json:"gender,omitempty"`
	Address        string         `gorm:"type:text" json:"address,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Patient) TableName() string { return "patients" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Patient) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// domain/repositories/contracts/patientRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// PatientRepository defines the persistence contract for Patient entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type PatientRepository interface {
	Create(dto dtos.PatientDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.PatientDTO, error)
	FindByCPF(cpf string) (*dtos.PatientDTO, error)
	List(page, limit int) ([]dtos.PatientDTO, error)
	Update(id uuid.UUID, dto dtos.PatientDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> repositories

// infra/repositories/patientRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// PatientRepositoryImpl implements contracts.PatientRepository using GORM.
type PatientRepositoryImpl struct {
	db *gorm.DB
}

// NewPatientRepository constructs a new PatientRepositoryImpl.
func NewPatientRepository(db *gorm.DB) *PatientRepositoryImpl {
	return &PatientRepositoryImpl{db: db}
}

func (r *PatientRepositoryImpl) Create(dto dtos.PatientDTO) (uuid.UUID, error) {
	entity := entities.Patient{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("patient create: %w", err)
	}
	return entity.ID, nil
}

func (r *PatientRepositoryImpl) FindByID(id uuid.UUID) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient not found: %s", id)
		}
		return nil, fmt.Errorf("patient find: %w", err)
	}
	dto := dtos.PatientDTO{} // map entity → DTO
	return &dto, nil
}

func (r *PatientRepositoryImpl) FindByCPF(cpf string) (*dtos.PatientDTO, error) {
	var entity entities.Patient
	if err := r.db.Where("cpf = ?", cpf).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("patient with cpf=%s not found", cpf)
		}
		return nil, fmt.Errorf("patient find by cpf: %w", err)
	}
	dto := dtos.PatientDTO{}
	return &dto, nil
}

func (r *PatientRepositoryImpl) List(page, limit int) ([]dtos.PatientDTO, error) {
	var entities []entities.Patient
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("patient list: %w", err)
	}
	dtos := make([]dtos.PatientDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *PatientRepositoryImpl) Update(id uuid.UUID, dto dtos.PatientDTO) error {
	result := r.db.Model(&entities.Patient{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("patient update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}

func (r *PatientRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Patient{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("patient delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("patient not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> entities

// domain/entities/doctor.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Doctor represents the doctors database table.
type Doctor struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	FullName       string    `gorm:"type:varchar(255);not null" json:"full_name" validate:"required"`
	CPF            string    `gorm:"type:varchar(14);uniqueIndex" json:"cpf"`
	CRM            string    `gorm:"type:varchar(20);uniqueIndex" json:"crm"`
	Specialty      string    `gorm:"type:varchar(100);not null" json:"specialty" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex" json:"email" validate:"required,email"`
	Phone          string    `gorm:"type:varchar(20)" json:"phone,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Doctor) TableName() string { return "doctors" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Doctor) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// domain/repositories/contracts/doctorRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// DoctorRepository defines the persistence contract for Doctor entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type DoctorRepository interface {
	Create(dto dtos.DoctorDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.DoctorDTO, error)
	FindByCRM(crm string) (*dtos.DoctorDTO, error)
	List(page, limit int) ([]dtos.DoctorDTO, error)
	Update(id uuid.UUID, dto dtos.DoctorDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> repositories

// infra/repositories/doctorRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.
type DoctorRepositoryImpl struct {
	db *gorm.DB
}

// NewDoctorRepository constructs a new DoctorRepositoryImpl.
func NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {
	return &DoctorRepositoryImpl{db: db}
}

func (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {
	entity := entities.Doctor{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("doctor create: %w", err)
	}
	return entity.ID, nil
}

func (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor not found: %s", id)
		}
		return nil, fmt.Errorf("doctor find: %w", err)
	}
	dto := dtos.DoctorDTO{} // map entity → DTO
	return &dto, nil
}

func (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.Where("crm = ?", crm).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor with crm=%s not found", crm)
		}
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	dto := dtos.DoctorDTO{}
	return &dto, nil
}

func (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {
	var entities []entities.Doctor
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("doctor list: %w", err)
	}
	dtos := make([]dtos.DoctorDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {
	result := r.db.Model(&entities.Doctor{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("doctor update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}

func (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Doctor{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("doctor delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> entities

// domain/entities/doctor.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Doctor represents the doctors database table.
type Doctor struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	FullName       string    `gorm:"type:varchar(255);not null" json:"full_name" validate:"required"`
	CPF            string    `gorm:"type:varchar(14);uniqueIndex" json:"cpf"`
	CRM            string    `gorm:"type:varchar(20);uniqueIndex" json:"crm"`
	Specialty      string    `gorm:"type:varchar(100);not null" json:"specialty" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex" json:"email" validate:"required,email"`
	Phone          string    `gorm:"type:varchar(20)" json:"phone,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Doctor) TableName() string { return "doctors" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Doctor) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// domain/repositories/contracts/doctorRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// DoctorRepository defines the persistence contract for Doctor entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type DoctorRepository interface {
	Create(dto dtos.DoctorDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.DoctorDTO, error)
	FindByCRM(crm string) (*dtos.DoctorDTO, error)
	List(page, limit int) ([]dtos.DoctorDTO, error)
	Update(id uuid.UUID, dto dtos.DoctorDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> repositories

// infra/repositories/doctorRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.
type DoctorRepositoryImpl struct {
	db *gorm.DB
}

// NewDoctorRepository constructs a new DoctorRepositoryImpl.
func NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {
	return &DoctorRepositoryImpl{db: db}
}

func (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {
	entity := entities.Doctor{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("doctor create: %w", err)
	}
	return entity.ID, nil
}

func (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor not found: %s", id)
		}
		return nil, fmt.Errorf("doctor find: %w", err)
	}
	dto := dtos.DoctorDTO{} // map entity → DTO
	return &dto, nil
}

func (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.Where("crm = ?", crm).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor with crm=%s not found", crm)
		}
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	dto := dtos.DoctorDTO{}
	return &dto, nil
}

func (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {
	var entities []entities.Doctor
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("doctor list: %w", err)
	}
	dtos := make([]dtos.DoctorDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {
	result := r.db.Model(&entities.Doctor{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("doctor update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}

func (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Doctor{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("doctor delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> entities

// domain/entities/doctor.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Doctor represents the doctors database table.
type Doctor struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	FullName       string    `gorm:"type:varchar(255);not null" json:"full_name" validate:"required"`
	CPF            string    `gorm:"type:varchar(14);uniqueIndex" json:"cpf"`
	CRM            string    `gorm:"type:varchar(20);uniqueIndex" json:"crm"`
	Specialty      string    `gorm:"type:varchar(100);not null" json:"specialty" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex" json:"email" validate:"required,email"`
	Phone          string    `gorm:"type:varchar(20)" json:"phone,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Doctor) TableName() string { return "doctors" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Doctor) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// domain/repositories/contracts/doctorRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// DoctorRepository defines the persistence contract for Doctor entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type DoctorRepository interface {
	Create(dto dtos.DoctorDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.DoctorDTO, error)
	FindByCRM(crm string) (*dtos.DoctorDTO, error)
	List(page, limit int) ([]dtos.DoctorDTO, error)
	Update(id uuid.UUID, dto dtos.DoctorDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> repositories

// infra/repositories/doctorRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.
type DoctorRepositoryImpl struct {
	db *gorm.DB
}

// NewDoctorRepository constructs a new DoctorRepositoryImpl.
func NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {
	return &DoctorRepositoryImpl{db: db}
}

func (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {
	entity := entities.Doctor{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("doctor create: %w", err)
	}
	return entity.ID, nil
}

func (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor not found: %s", id)
		}
		return nil, fmt.Errorf("doctor find: %w", err)
	}
	dto := dtos.DoctorDTO{} // map entity → DTO
	return &dto, nil
}

func (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.Where("crm = ?", crm).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor with crm=%s not found", crm)
		}
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	dto := dtos.DoctorDTO{}
	return &dto, nil
}

func (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {
	var entities []entities.Doctor
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("doctor list: %w", err)
	}
	dtos := make([]dtos.DoctorDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {
	result := r.db.Model(&entities.Doctor{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("doctor update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}

func (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Doctor{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("doctor delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> entities

// domain/entities/doctor.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Doctor represents the doctors database table.
type Doctor struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	FullName       string    `gorm:"type:varchar(255);not null" json:"full_name" validate:"required"`
	CPF            string    `gorm:"type:varchar(14);uniqueIndex" json:"cpf"`
	CRM            string    `gorm:"type:varchar(20);uniqueIndex" json:"crm"`
	Specialty      string    `gorm:"type:varchar(100);not null" json:"specialty" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex" json:"email" validate:"required,email"`
	Phone          string    `gorm:"type:varchar(20)" json:"phone,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Doctor) TableName() string { return "doctors" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Doctor) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// domain/repositories/contracts/doctorRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// DoctorRepository defines the persistence contract for Doctor entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type DoctorRepository interface {
	Create(dto dtos.DoctorDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.DoctorDTO, error)
	FindByCRM(crm string) (*dtos.DoctorDTO, error)
	List(page, limit int) ([]dtos.DoctorDTO, error)
	Update(id uuid.UUID, dto dtos.DoctorDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> repositories

// infra/repositories/doctorRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// DoctorRepositoryImpl implements contracts.DoctorRepository using GORM.
type DoctorRepositoryImpl struct {
	db *gorm.DB
}

// NewDoctorRepository constructs a new DoctorRepositoryImpl.
func NewDoctorRepository(db *gorm.DB) *DoctorRepositoryImpl {
	return &DoctorRepositoryImpl{db: db}
}

func (r *DoctorRepositoryImpl) Create(dto dtos.DoctorDTO) (uuid.UUID, error) {
	entity := entities.Doctor{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("doctor create: %w", err)
	}
	return entity.ID, nil
}

func (r *DoctorRepositoryImpl) FindByID(id uuid.UUID) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor not found: %s", id)
		}
		return nil, fmt.Errorf("doctor find: %w", err)
	}
	dto := dtos.DoctorDTO{} // map entity → DTO
	return &dto, nil
}

func (r *DoctorRepositoryImpl) FindByCRM(crm string) (*dtos.DoctorDTO, error) {
	var entity entities.Doctor
	if err := r.db.Where("crm = ?", crm).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("doctor with crm=%s not found", crm)
		}
		return nil, fmt.Errorf("doctor find by crm: %w", err)
	}
	dto := dtos.DoctorDTO{}
	return &dto, nil
}

func (r *DoctorRepositoryImpl) List(page, limit int) ([]dtos.DoctorDTO, error) {
	var entities []entities.Doctor
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("doctor list: %w", err)
	}
	dtos := make([]dtos.DoctorDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *DoctorRepositoryImpl) Update(id uuid.UUID, dto dtos.DoctorDTO) error {
	result := r.db.Model(&entities.Doctor{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("doctor update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}

func (r *DoctorRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Doctor{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("doctor delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("doctor not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> entities

// domain/entities/appointment.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Appointment represents the appointments database table.
type Appointment struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID  uuid.UUID  `gorm:"type:uuid;not null;index" json:"organization_id"`
	PatientID       uuid.UUID  `gorm:"type:uuid;not null;index" json:"patient_id"`
	DoctorID        uuid.UUID  `gorm:"type:uuid;not null;index" json:"doctor_id"`
	UserID          uuid.UUID  `gorm:"type:uuid;not null" json:"user_id"`
	Specialization  string     `gorm:"type:varchar(100);not null" json:"specialization"`
	DateTime        time.Time  `gorm:"type:timestamptz;not null" json:"date_time"`
	Status          string     `gorm:"type:varchar(50);default:scheduled" json:"status"`
	Notes           string     `gorm:"type:text" json:"notes,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Appointment) TableName() string { return "appointments" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Appointment) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// domain/repositories/contracts/appointmentRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// AppointmentRepository defines the persistence contract for Appointment entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type AppointmentRepository interface {
	Create(dto dtos.AppointmentDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)
	FindByStatus(status string) (*dtos.AppointmentDTO, error)
	List(page, limit int) ([]dtos.AppointmentDTO, error)
	Update(id uuid.UUID, dto dtos.AppointmentDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> repositories

// infra/repositories/appointmentRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.
type AppointmentRepositoryImpl struct {
	db *gorm.DB
}

// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.
func NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {
	return &AppointmentRepositoryImpl{db: db}
}

func (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {
	entity := entities.Appointment{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("appointment create: %w", err)
	}
	return entity.ID, nil
}

func (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment not found: %s", id)
		}
		return nil, fmt.Errorf("appointment find: %w", err)
	}
	dto := dtos.AppointmentDTO{} // map entity → DTO
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.Where("status = ?", status).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment with status=%s not found", status)
		}
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	dto := dtos.AppointmentDTO{}
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {
	var entities []entities.Appointment
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("appointment list: %w", err)
	}
	dtos := make([]dtos.AppointmentDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {
	result := r.db.Model(&entities.Appointment{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("appointment update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}

func (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Appointment{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("appointment delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> entities

// domain/entities/appointment.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Appointment represents the appointments database table.
type Appointment struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID  uuid.UUID  `gorm:"type:uuid;not null;index" json:"organization_id"`
	PatientID       uuid.UUID  `gorm:"type:uuid;not null;index" json:"patient_id"`
	DoctorID        uuid.UUID  `gorm:"type:uuid;not null;index" json:"doctor_id"`
	UserID          uuid.UUID  `gorm:"type:uuid;not null" json:"user_id"`
	Specialization  string     `gorm:"type:varchar(100);not null" json:"specialization"`
	DateTime        time.Time  `gorm:"type:timestamptz;not null" json:"date_time"`
	Status          string     `gorm:"type:varchar(50);default:scheduled" json:"status"`
	Notes           string     `gorm:"type:text" json:"notes,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Appointment) TableName() string { return "appointments" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Appointment) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// domain/repositories/contracts/appointmentRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// AppointmentRepository defines the persistence contract for Appointment entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type AppointmentRepository interface {
	Create(dto dtos.AppointmentDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)
	FindByStatus(status string) (*dtos.AppointmentDTO, error)
	List(page, limit int) ([]dtos.AppointmentDTO, error)
	Update(id uuid.UUID, dto dtos.AppointmentDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> repositories

// infra/repositories/appointmentRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.
type AppointmentRepositoryImpl struct {
	db *gorm.DB
}

// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.
func NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {
	return &AppointmentRepositoryImpl{db: db}
}

func (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {
	entity := entities.Appointment{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("appointment create: %w", err)
	}
	return entity.ID, nil
}

func (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment not found: %s", id)
		}
		return nil, fmt.Errorf("appointment find: %w", err)
	}
	dto := dtos.AppointmentDTO{} // map entity → DTO
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.Where("status = ?", status).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment with status=%s not found", status)
		}
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	dto := dtos.AppointmentDTO{}
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {
	var entities []entities.Appointment
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("appointment list: %w", err)
	}
	dtos := make([]dtos.AppointmentDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {
	result := r.db.Model(&entities.Appointment{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("appointment update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}

func (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Appointment{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("appointment delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> entities

// domain/entities/appointment.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Appointment represents the appointments database table.
type Appointment struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID  uuid.UUID  `gorm:"type:uuid;not null;index" json:"organization_id"`
	PatientID       uuid.UUID  `gorm:"type:uuid;not null;index" json:"patient_id"`
	DoctorID        uuid.UUID  `gorm:"type:uuid;not null;index" json:"doctor_id"`
	UserID          uuid.UUID  `gorm:"type:uuid;not null" json:"user_id"`
	Specialization  string     `gorm:"type:varchar(100);not null" json:"specialization"`
	DateTime        time.Time  `gorm:"type:timestamptz;not null" json:"date_time"`
	Status          string     `gorm:"type:varchar(50);default:scheduled" json:"status"`
	Notes           string     `gorm:"type:text" json:"notes,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Appointment) TableName() string { return "appointments" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Appointment) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// domain/repositories/contracts/appointmentRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// AppointmentRepository defines the persistence contract for Appointment entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type AppointmentRepository interface {
	Create(dto dtos.AppointmentDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)
	FindByStatus(status string) (*dtos.AppointmentDTO, error)
	List(page, limit int) ([]dtos.AppointmentDTO, error)
	Update(id uuid.UUID, dto dtos.AppointmentDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> repositories

// infra/repositories/appointmentRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.
type AppointmentRepositoryImpl struct {
	db *gorm.DB
}

// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.
func NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {
	return &AppointmentRepositoryImpl{db: db}
}

func (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {
	entity := entities.Appointment{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("appointment create: %w", err)
	}
	return entity.ID, nil
}

func (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment not found: %s", id)
		}
		return nil, fmt.Errorf("appointment find: %w", err)
	}
	dto := dtos.AppointmentDTO{} // map entity → DTO
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.Where("status = ?", status).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment with status=%s not found", status)
		}
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	dto := dtos.AppointmentDTO{}
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {
	var entities []entities.Appointment
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("appointment list: %w", err)
	}
	dtos := make([]dtos.AppointmentDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {
	result := r.db.Model(&entities.Appointment{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("appointment update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}

func (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Appointment{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("appointment delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> entities

// domain/entities/appointment.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// Appointment represents the appointments database table.
type Appointment struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID  uuid.UUID  `gorm:"type:uuid;not null;index" json:"organization_id"`
	PatientID       uuid.UUID  `gorm:"type:uuid;not null;index" json:"patient_id"`
	DoctorID        uuid.UUID  `gorm:"type:uuid;not null;index" json:"doctor_id"`
	UserID          uuid.UUID  `gorm:"type:uuid;not null" json:"user_id"`
	Specialization  string     `gorm:"type:varchar(100);not null" json:"specialization"`
	DateTime        time.Time  `gorm:"type:timestamptz;not null" json:"date_time"`
	Status          string     `gorm:"type:varchar(50);default:scheduled" json:"status"`
	Notes           string     `gorm:"type:text" json:"notes,omitempty"`
}

// TableName overrides GORM's convention-based table name.
func (Appointment) TableName() string { return "appointments" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *Appointment) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// domain/repositories/contracts/appointmentRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// AppointmentRepository defines the persistence contract for Appointment entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type AppointmentRepository interface {
	Create(dto dtos.AppointmentDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error)
	FindByStatus(status string) (*dtos.AppointmentDTO, error)
	List(page, limit int) ([]dtos.AppointmentDTO, error)
	Update(id uuid.UUID, dto dtos.AppointmentDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> repositories

// infra/repositories/appointmentRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// AppointmentRepositoryImpl implements contracts.AppointmentRepository using GORM.
type AppointmentRepositoryImpl struct {
	db *gorm.DB
}

// NewAppointmentRepository constructs a new AppointmentRepositoryImpl.
func NewAppointmentRepository(db *gorm.DB) *AppointmentRepositoryImpl {
	return &AppointmentRepositoryImpl{db: db}
}

func (r *AppointmentRepositoryImpl) Create(dto dtos.AppointmentDTO) (uuid.UUID, error) {
	entity := entities.Appointment{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("appointment create: %w", err)
	}
	return entity.ID, nil
}

func (r *AppointmentRepositoryImpl) FindByID(id uuid.UUID) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment not found: %s", id)
		}
		return nil, fmt.Errorf("appointment find: %w", err)
	}
	dto := dtos.AppointmentDTO{} // map entity → DTO
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) FindByStatus(status string) (*dtos.AppointmentDTO, error) {
	var entity entities.Appointment
	if err := r.db.Where("status = ?", status).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("appointment with status=%s not found", status)
		}
		return nil, fmt.Errorf("appointment find by status: %w", err)
	}
	dto := dtos.AppointmentDTO{}
	return &dto, nil
}

func (r *AppointmentRepositoryImpl) List(page, limit int) ([]dtos.AppointmentDTO, error) {
	var entities []entities.Appointment
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("appointment list: %w", err)
	}
	dtos := make([]dtos.AppointmentDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *AppointmentRepositoryImpl) Update(id uuid.UUID, dto dtos.AppointmentDTO) error {
	result := r.db.Model(&entities.Appointment{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("appointment update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}

func (r *AppointmentRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.Appointment{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("appointment delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("appointment not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> entities

// domain/entities/user.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// User represents the users database table.
type User struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string    `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex;not null" json:"email" validate:"required,email"`
	Password       string    `gorm:"type:varchar(255);not null" json:"-"` // never serialised
	Role           string    `gorm:"type:varchar(50);not null;default:user" json:"role" validate:"required,oneof=admin user doctor nurse"`
}

// TableName overrides GORM's convention-based table name.
func (User) TableName() string { return "users" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *User) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> contracts

// domain/repositories/contracts/userRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// UserRepository defines the persistence contract for User entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type UserRepository interface {
	Create(dto dtos.UserDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.UserDTO, error)
	FindByEmail(email string) (*dtos.UserDTO, error)
	List(page, limit int) ([]dtos.UserDTO, error)
	Update(id uuid.UUID, dto dtos.UserDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.21
<go_pkg> repositories

// infra/repositories/userRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// UserRepositoryImpl implements contracts.UserRepository using GORM.
type UserRepositoryImpl struct {
	db *gorm.DB
}

// NewUserRepository constructs a new UserRepositoryImpl.
func NewUserRepository(db *gorm.DB) *UserRepositoryImpl {
	return &UserRepositoryImpl{db: db}
}

func (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {
	entity := entities.User{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("user create: %w", err)
	}
	return entity.ID, nil
}

func (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user not found: %s", id)
		}
		return nil, fmt.Errorf("user find: %w", err)
	}
	dto := dtos.UserDTO{} // map entity → DTO
	return &dto, nil
}

func (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.Where("email = ?", email).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user with email=%s not found", email)
		}
		return nil, fmt.Errorf("user find by email: %w", err)
	}
	dto := dtos.UserDTO{}
	return &dto, nil
}

func (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {
	var entities []entities.User
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("user list: %w", err)
	}
	dtos := make([]dtos.UserDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {
	result := r.db.Model(&entities.User{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("user update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}

func (r *UserRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.User{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("user delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> entities

// domain/entities/user.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// User represents the users database table.
type User struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string    `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex;not null" json:"email" validate:"required,email"`
	Password       string    `gorm:"type:varchar(255);not null" json:"-"` // never serialised
	Role           string    `gorm:"type:varchar(50);not null;default:user" json:"role" validate:"required,oneof=admin user doctor nurse"`
}

// TableName overrides GORM's convention-based table name.
func (User) TableName() string { return "users" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *User) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> contracts

// domain/repositories/contracts/userRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// UserRepository defines the persistence contract for User entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type UserRepository interface {
	Create(dto dtos.UserDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.UserDTO, error)
	FindByEmail(email string) (*dtos.UserDTO, error)
	List(page, limit int) ([]dtos.UserDTO, error)
	Update(id uuid.UUID, dto dtos.UserDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.22
<go_pkg> repositories

// infra/repositories/userRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// UserRepositoryImpl implements contracts.UserRepository using GORM.
type UserRepositoryImpl struct {
	db *gorm.DB
}

// NewUserRepository constructs a new UserRepositoryImpl.
func NewUserRepository(db *gorm.DB) *UserRepositoryImpl {
	return &UserRepositoryImpl{db: db}
}

func (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {
	entity := entities.User{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("user create: %w", err)
	}
	return entity.ID, nil
}

func (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user not found: %s", id)
		}
		return nil, fmt.Errorf("user find: %w", err)
	}
	dto := dtos.UserDTO{} // map entity → DTO
	return &dto, nil
}

func (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.Where("email = ?", email).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user with email=%s not found", email)
		}
		return nil, fmt.Errorf("user find by email: %w", err)
	}
	dto := dtos.UserDTO{}
	return &dto, nil
}

func (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {
	var entities []entities.User
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("user list: %w", err)
	}
	dtos := make([]dtos.UserDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {
	result := r.db.Model(&entities.User{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("user update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}

func (r *UserRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.User{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("user delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> entities

// domain/entities/user.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// User represents the users database table.
type User struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string    `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex;not null" json:"email" validate:"required,email"`
	Password       string    `gorm:"type:varchar(255);not null" json:"-"` // never serialised
	Role           string    `gorm:"type:varchar(50);not null;default:user" json:"role" validate:"required,oneof=admin user doctor nurse"`
}

// TableName overrides GORM's convention-based table name.
func (User) TableName() string { return "users" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *User) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> contracts

// domain/repositories/contracts/userRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// UserRepository defines the persistence contract for User entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type UserRepository interface {
	Create(dto dtos.UserDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.UserDTO, error)
	FindByEmail(email string) (*dtos.UserDTO, error)
	List(page, limit int) ([]dtos.UserDTO, error)
	Update(id uuid.UUID, dto dtos.UserDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>


<go_file>
<go_version> go1.23
<go_pkg> repositories

// infra/repositories/userRepository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories

import (
	"fmt"

	"medical-sas-api/domain/dtos"
	"medical-sas-api/domain/entities"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

// UserRepositoryImpl implements contracts.UserRepository using GORM.
type UserRepositoryImpl struct {
	db *gorm.DB
}

// NewUserRepository constructs a new UserRepositoryImpl.
func NewUserRepository(db *gorm.DB) *UserRepositoryImpl {
	return &UserRepositoryImpl{db: db}
}

func (r *UserRepositoryImpl) Create(dto dtos.UserDTO) (uuid.UUID, error) {
	entity := entities.User{} // map DTO → entity fields
	// (field mapping omitted for brevity — use a mapper or manual assignment)
	if err := r.db.Create(&entity).Error; err != nil {
		return uuid.Nil, fmt.Errorf("user create: %w", err)
	}
	return entity.ID, nil
}

func (r *UserRepositoryImpl) FindByID(id uuid.UUID) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.First(&entity, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user not found: %s", id)
		}
		return nil, fmt.Errorf("user find: %w", err)
	}
	dto := dtos.UserDTO{} // map entity → DTO
	return &dto, nil
}

func (r *UserRepositoryImpl) FindByEmail(email string) (*dtos.UserDTO, error) {
	var entity entities.User
	if err := r.db.Where("email = ?", email).First(&entity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("user with email=%s not found", email)
		}
		return nil, fmt.Errorf("user find by email: %w", err)
	}
	dto := dtos.UserDTO{}
	return &dto, nil
}

func (r *UserRepositoryImpl) List(page, limit int) ([]dtos.UserDTO, error) {
	var entities []entities.User
	offset := (page - 1) * limit
	if err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
		return nil, fmt.Errorf("user list: %w", err)
	}
	dtos := make([]dtos.UserDTO, 0, len(entities))
	// map each entity to DTO
	return dtos, nil
}

func (r *UserRepositoryImpl) Update(id uuid.UUID, dto dtos.UserDTO) error {
	result := r.db.Model(&entities.User{}).Where("id = ?", id).Updates(dto)
	if result.Error != nil {
		return fmt.Errorf("user update: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}

func (r *UserRepositoryImpl) Delete(id uuid.UUID) error {
	// GORM soft delete: sets deleted_at instead of removing the row
	result := r.db.Delete(&entities.User{}, "id = ?", id)
	if result.Error != nil {
		return fmt.Errorf("user delete: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("user not found: %s", id)
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> entities

// domain/entities/user.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// User represents the users database table.
type User struct {
	ID        uuid.UUID      `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at"`
	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
	OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
	Name           string    `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
	Email          string    `gorm:"type:varchar(255);uniqueIndex;not null" json:"email" validate:"required,email"`
	Password       string    `gorm:"type:varchar(255);not null" json:"-"` // never serialised
	Role           string    `gorm:"type:varchar(50);not null;default:user" json:"role" validate:"required,oneof=admin user doctor nurse"`
}

// TableName overrides GORM's convention-based table name.
func (User) TableName() string { return "users" }

// BeforeCreate sets a UUID before the record is inserted.
func (e *User) BeforeCreate(tx *gorm.DB) error {
	if e.ID == uuid.Nil {
		e.ID = uuid.New()
	}
	return nil
}
</go_file>


<go_file>
<go_version> go1.24
<go_pkg> contracts

// domain/repositories/contracts/userRepository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts

import (
	"medical-sas-api/domain/dtos"
	"github.com/google/uuid"
)

// UserRepository defines the persistence contract for User entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type UserRepository interface {
	Create(dto dtos.UserDTO) (uuid.UUID, error)
	FindByID(id uuid.UUID) (*dtos.UserDTO, error)
	FindByEmail(email string) (*dtos.UserDTO, error)
	List(page, limit int) ([]dtos.UserDTO, error)
	Update(id uuid.UUID, dto dtos.UserDTO) error
	Delete(id uuid.UUID) error // soft delete
}
</go_file>