Files
dance-lessons-coach/adr/0014-grpc-adoption-strategy.md
Gabriel Radureau b391534f2d 🤖 feat: implement trunk-based CI/CD with local testing
- Designed trunk-based development workflow with branch protection
- Added workflow validation job to prevent main branch breaks
- Integrated act (GitHub Actions runner) for local Gitea workflow testing
- Created unified CI/CD script interface (scripts/cicd.sh)
- Added YAML lint configuration with practical limits (400 chars)
- Organized all CI/CD scripts under scripts/cicd/ directory
- Confirmed Gitea/GitHub Actions compatibility via local testing
- Updated ADR 0017 with implementation details and test results
- Enhanced documentation with local development workflow

See ADR-0017 for complete trunk-based development workflow documentation.
See ADR-0016 for CI/CD pipeline design.
2026-04-05 23:07:32 +02:00

7.5 KiB

14. gRPC Adoption Strategy

Date: 2026-04-05 Status: Accepted Authors: Arcodange Team

Context

The DanceLessonsCoach project currently uses REST/JSON for all API communication. As the project evolves, we need to determine when and how to adopt gRPC for performance-critical and internal communication scenarios.

Decision Drivers

  • Current Needs: Simple API with good REST support
  • Future Growth: Potential for mobile apps and microservices
  • Performance: Current REST performance is adequate
  • Complexity: gRPC adds significant architectural complexity
  • Team Expertise: Strong REST/JSON experience, limited gRPC experience
  • Ecosystem: Existing tooling and documentation for REST

Considered Options

Option 1: Immediate Full gRPC Adoption

Description: Replace all REST endpoints with gRPC immediately

Pros:

  • Future-proof architecture
  • Best performance from day one
  • Clean slate design

Cons:

  • Significant development effort
  • Steep learning curve
  • Breaking changes for existing clients
  • Overkill for current needs

Description: Keep REST for public API, add gRPC for internal/services

Pros:

  • Backward compatibility maintained
  • Gradual learning curve
  • Performance where needed
  • Flexibility for future growth

Cons:

  • More complex architecture
  • Need to maintain both protocols
  • Gateway translation overhead

Option 3: REST Only

Description: Continue with REST/JSON only

Pros:

  • Simple and well-understood
  • Good tooling and debugging
  • No architectural changes needed

Cons:

  • May limit future scalability
  • Performance ceiling
  • Harder to add real-time features

Option 4: gRPC for New Features Only

Description: Use REST for existing, gRPC for new features

Pros:

  • No breaking changes
  • Learn gRPC gradually
  • Performance for new features

Cons:

  • Inconsistent API surface
  • Complex migration path
  • Harder to maintain coherence

Decision Outcome

Chosen option: Option 2 - Hybrid Approach

Implementation Strategy

Phase 1: Preparation (Current)

  • Document gRPC adoption strategy (this ADR)
  • Implement OpenAPI/Swagger for REST (ADR-0013)
  • Continue REST development
  • Monitor performance metrics

Phase 2: Foundation (When Needed)

# Add gRPC dependencies
go get google.golang.org/grpc
go get google.golang.org/protobuf

# Create proto directory
mkdir -p proto/greet

# Add basic protobuf definition
cat > proto/greet/greet.proto << 'EOF'
syntax = "proto3";

package greet.v1;

service GreetService {
  rpc Greet (GreetRequest) returns (GreetResponse);
}

message GreetRequest {
  string name = 1;
}

message GreetResponse {
  string message = 1;
}
EOF

Phase 3: Internal Services (Future)

// When adding internal services:
// User Service <--gRPC--> Greet Service
// Analytics Service <--gRPC--> Greet Service

func (s *Server) startGRPC() {
    if s.config.GRPC.Enabled {
        lis, err := net.Listen("tcp", s.config.GRPC.Address)
        if err != nil {
            log.Error().Err(err).Msg("Failed to listen for gRPC")
            return
        }
        
        grpcServer := grpc.NewServer()
        proto.RegisterGreetServiceServer(grpcServer, s.grpcHandler)
        
        log.Info().Str("address", s.config.GRPC.Address).Msg("Starting gRPC server")
        if err := grpcServer.Serve(lis); err != nil {
            log.Error().Err(err).Msg("gRPC server failed")
        }
    }
}

Phase 4: Mobile Clients (Future)

# When adding mobile apps:
# iOS/Android App --gRPC--> DanceLessonsCoach

# Generate mobile clients
protoc --plugin=protoc-gen-grpc=`which grpc_swift_plugin` \
       --grpc_swift_out=. \
       proto/greet/greet.proto

Consequences

Positive

  1. Backward Compatibility: Existing REST clients continue working
  2. Performance: gRPC available when needed for critical paths
  3. Flexibility: Can choose right protocol for each use case
  4. Gradual Learning: Team can learn gRPC at appropriate pace
  5. Future-Proof: Architecture ready for growth

Negative

  1. Complexity: More moving parts to maintain
  2. Overhead: Gateway translation between protocols
  3. Learning Curve: Team needs to learn gRPC eventually
  4. Build Complexity: Additional build steps for protobuf

Mitigations

  1. Documentation: Comprehensive gRPC guides and examples
  2. Training: Gradual team education on gRPC concepts
  3. Tooling: Automate protobuf generation in CI/CD
  4. Monitoring: Track protocol usage and performance

Verification

Success Criteria

  1. REST API remains fully functional
  2. gRPC can be enabled via configuration
  3. No performance regression in REST paths
  4. Clear documentation for both protocols
  5. CI/CD supports both REST and gRPC testing

Test Plan

# Test REST still works
curl http://localhost:8080/api/v1/greet/John
# Expected: {"message":"Hello John!"}

# Test gRPC can be disabled by default
export DLC_GRPC_ENABLED=false
./bin/server
# Expected: Only REST server starts

# Test configuration validation
DLC_GRPC_ENABLED=true DLC_GRPC_PORT=invalid ./bin/server
# Expected: Configuration error, clean exit

Future Triggers

Consider implementing gRPC when any of these occur:

  1. Mobile App Development: Need for efficient mobile communication
  2. Microservices: Adding internal services that need gRPC
  3. Performance Issues: REST becomes bottleneck at scale
  4. Real-time Features: Need for streaming/bidirectional communication
  5. Team Readiness: Team comfortable with gRPC concepts

Revision History

  • 1.0 (2026-04-05): Initial decision
  • 1.1 (2026-04-05): Added implementation phases and triggers

References

Approved by: Arcodange Team Effective Date: 2026-04-05

Configuration Reference

# config.yaml example for future gRPC support
grpc:
  enabled: false  # Set to true to enable gRPC server
  host: "0.0.0.0"
  port: "50051"
  reflection: true  # Enable for development
  max_msg_size: 4194304  # 4MB max message size

rest:
  enabled: true  # REST remains enabled
  host: "0.0.0.0"
  port: "8080"

Migration Checklist

  • Add gRPC dependencies to go.mod
  • Create proto directory structure
  • Add basic greet.proto definition
  • Implement gRPC server (disabled by default)
  • Add configuration options
  • Update CI/CD for protobuf generation
  • Add gRPC health checks
  • Document gRPC usage
  • Performance benchmarking
  • Gradual rollout to production

Monitoring Metrics

Recommended metrics to track:

# REST metrics
rest_requests_total{endpoint="/api/v1/greet", status="200"}
rest_response_time_seconds{quantile="0.95"}

# gRPC metrics (when enabled)
grpc_server_handling_seconds{grpc_method="Greet", grpc_code="OK"}
grpc_server_started_total{grpc_method="Greet"}

# Comparison metrics
api_latency_comparison{protocol="rest", endpoint="/greet"}
api_latency_comparison{protocol="grpc", endpoint="/greet"}