Note: The Family-Service repository implements a GraphQL service using ServiceLib. It serves as an excellent illustration of ServiceLib's capabilities and potential.
ServiceLib is a comprehensive Go library designed to accelerate the development of robust, production-ready microservices. It provides a collection of reusable components and utilities that address common challenges in service development, allowing developers to focus on business logic rather than infrastructure concerns.
The library follows modern Go practices and design patterns, with a focus on:
- Modularity: Each component can be used independently or together with others
- Testability: All components are designed with testing in mind
- Performance: Optimized for high-throughput microservices
- Reliability: Built-in error handling and recovery mechanisms
- Observability: Integrated logging, metrics, and tracing
- Authentication & Authorization: Secure your services with JWT, OIDC, and role-based access control
- Configuration Management: Flexible configuration with support for multiple sources and formats
- Error Handling: Structured error types with context, stack traces, and categorization
- Database Access: Connection management, health checks, and transaction support
- Dependency Injection: Simple yet powerful DI container for managing service dependencies
- Telemetry: Integrated logging, metrics, and distributed tracing
- Health Checks: Standardized health check endpoints and status reporting
- Middleware: Common HTTP middleware for logging, error handling, and more
- Retry & Circuit Breaking: Resilience patterns for handling transient failures
- Validation: Comprehensive input validation utilities
go get github.com/abitofhelp/servicelib
Here's a simple example of creating an HTTP server with ServiceLib:
package main
import (
"context"
"log"
"net/http"
"github.com/abitofhelp/servicelib/logging"
"github.com/abitofhelp/servicelib/middleware"
"go.uber.org/zap"
)
func main() {
// Create a logger
logger, err := logging.NewLogger("info", true)
if err != nil {
log.Fatalf("Failed to initialize logger: %v", err)
}
defer logger.Sync()
// Create a context logger
contextLogger := logging.NewContextLogger(logger)
// Create a simple HTTP handler
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, ServiceLib!"))
})
// Add middleware for logging, metrics, and recovery
handler := middleware.Chain(
mux,
middleware.WithRequestID(context.Background()),
middleware.Logging(contextLogger),
middleware.Recovery(contextLogger),
)
// Start the server
contextLogger.Info(context.Background(), "Starting server", zap.String("address", ":8080"))
if err := http.ListenAndServe(":8080", handler); err != nil {
contextLogger.Fatal(context.Background(), "Server failed", zap.Error(err))
}
}
ServiceLib provides a comprehensive error handling system:
package main
import (
"context"
"database/sql"
"fmt"
"net/http"
"github.com/abitofhelp/servicelib/errors"
errhttp "github.com/abitofhelp/servicelib/errors/http"
errlog "github.com/abitofhelp/servicelib/errors/log"
"github.com/abitofhelp/servicelib/logging"
"go.uber.org/zap"
)
// GetUserByID retrieves a user by ID with proper error handling
func GetUserByID(ctx context.Context, logger *logging.ContextLogger, id string) (*User, error) {
// Validate input
if id == "" {
// Create a validation error
err := errors.NewValidationError("User ID is required", "id", nil)
// Log the error
errlog.LogError(ctx, logger, err)
return nil, err
}
// Simulate a database error
if id == "db-error" {
dbErr := sql.ErrNoRows
err := errors.NewDatabaseError("Failed to query user", "SELECT", "users", dbErr)
// Log the error
errlog.LogError(ctx, logger, err)
return nil, err
}
// Return a user
return &User{ID: id, Name: "John Doe"}, nil
}
// HTTP handler with error handling
func GetUserHandler(w http.ResponseWriter, r *http.Request, service *UserService) {
// Get user ID from request
id := r.URL.Query().Get("id")
// Get user from service
user, err := service.GetUserByID(r.Context(), id)
if err != nil {
// Write error response with appropriate status code
errhttp.WriteError(w, err)
return
}
// Write success response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"id":"%s","name":"%s"}`, user.ID, user.Name)
}
For more examples, see the Examples directory.
ServiceLib is organized into the following packages:
- auth - Authentication and authorization
- cache - Caching utilities
- circuit - Circuit breaker implementation
- config - Configuration management
- context - Context utilities
- date - Date and time utilities
- db - Database access and management
- di - Dependency injection
- env - Environment variable utilities
- errors - Error handling, management, and recovery patterns
- graphql - GraphQL utilities
- health - Health check utilities
- logging - Structured logging
- middleware - HTTP middleware
- model - Model utilities
- rate - Rate limiting
- repository - Repository pattern implementation
- retry - Retry utilities
- shutdown - Graceful shutdown utilities
- signal - Signal handling
- stringutil - String utilities
- telemetry - Telemetry (metrics, tracing)
- transaction - Transaction management
- validation - Input validation
- valueobject - Value object implementations
For complete, runnable examples of each component, see the EXAMPLES directory.
The following UML diagrams provide a visual representation of the ServiceLib architecture:
- Architecture Overview - High-level overview of the ServiceLib architecture based on Clean Architecture, DDD, and Hexagonal Architecture
- Package Diagram - Diagram showing the packages in ServiceLib and their relationships
- HTTP Request Sequence - Sequence diagram illustrating the HTTP request processing flow
- Errors Package Class Diagram - Class diagram for the errors package
Contributions to ServiceLib are welcome! Please see the Contributing Guide for more information.
This project is licensed under the MIT License - see the LICENSE file for details.