| // Package operation provides an abstraction for RPC-style APIs. | |
| // | |
| // Implementations are generally business logic functions that take a request and return a response. | |
| // Consumers of operations are transport layers, such as HTTP handlers or gRPC services. | |
| // | |
| // The operation layer allows you to separate business logic from transport concerns as well as to compose and reuse transport and operation-agnostic logic. | |
| package operation | |
| import ( | |
| "context" | |
| ) | |
| // Operation is the fundamental building block of RPC-style APIs. | |
| // It represents a single operation that can be performed by a caller. | |
| type Operation[Request any, Response any] func(ctx context.Context, request Request) (Response, error) | |
| // AsNoResponseOperation wraps a func (context.Context, request Request) error typed function as an operation | |
| // useful for Delete like methods | |
| func AsNoResponseOperation[Request any](f func(ctx context.Context, request Request) error) Operation[Request, any] { | |
| return func(ctx context.Context, request Request) (any, error) { | |
| return nil, f(ctx, request) | |
| } | |
| } | |
| // Compose can be used to chain two operations together (e.g. get something, then update it). | |
| func Compose[Request any, Intermediate any, Response any](op1 Operation[Request, Intermediate], op2 Operation[Intermediate, Response]) Operation[Request, Response] { | |
| return func(ctx context.Context, request Request) (Response, error) { | |
| intermediate, err := op1(ctx, request) | |
| if err != nil { | |
| var defaultResponse Response | |
| return defaultResponse, err | |
| } | |
| return op2(ctx, intermediate) | |
| } | |
| } | |