diff --git a/client/src/main/proto/spine/client/command_service.proto b/client/src/main/proto/spine/client/command_service.proto index 89f5746dd66..f4189a2f611 100644 --- a/client/src/main/proto/spine/client/command_service.proto +++ b/client/src/main/proto/spine/client/command_service.proto @@ -30,8 +30,6 @@ package spine.client; import "spine/options.proto"; option (type_url_prefix) = "type.spine.io"; -// We put gRPC-based classes into `grpc` sub-package, which is annotated as `@Internal` -// to hide implementation details from the public API of the framework. option java_package = "io.spine.client.grpc"; option java_multiple_files = true; option java_outer_classname = "CommandServiceProto"; @@ -39,9 +37,24 @@ option java_outer_classname = "CommandServiceProto"; import "spine/core/command.proto"; import "spine/core/ack.proto"; -// A service for sending commands from clients. +// A service for posting commands to the application backend. +// service CommandService { - // Request to handle a command. + // Posts the given command to the application backend. + // + // When the command is successfully received by the application, it responds with + // an acknowledgement. The received `Ack` may have one of the following statuses: + // + // 1. `Ok` meaning the command is accepted for further processing. + // 2. `Error` when a technical error occurs on the side of the application. + // 3. `Rejection` if no technical error occurred but due to the business rules the command + // should be immediately disqualified from being executed. A typical scenario would be + // when the permissions of a user who made a request aren't broad enough. + // + // In case of internal gRPC errors or issues on a transport layer, an `Ack` would not be + // received. All errors which occur on the side of gRCP should be handled by the means + // of the used client stub. + // rpc Post(core.Command) returns (core.Ack); } diff --git a/server/src/main/java/io/spine/server/CommandService.java b/server/src/main/java/io/spine/server/CommandService.java index 7a4fe9402b8..597c41e50a7 100644 --- a/server/src/main/java/io/spine/server/CommandService.java +++ b/server/src/main/java/io/spine/server/CommandService.java @@ -45,8 +45,13 @@ import static io.spine.server.bus.Acks.reject; /** - * The {@code CommandService} allows client applications to post commands and - * receive updates from the application backend. + * The {@code CommandService} provides a synchronous way to post commands + * to the application backend. + * + *

This class is an implementation of a corresponding gRPC service. Hence, public API of this + * class is dictated by the {@linkplain CommandServiceGrpc generated code}. Despite the fact of its + * "publicity", it's not meant to be used directly. Use {@link io.spine.client.Client Client} + * to post commands to the application. */ public final class CommandService extends CommandServiceGrpc.CommandServiceImplBase @@ -55,7 +60,7 @@ public final class CommandService private final ImmutableMap commandToContext; /** - * Constructs new instance using the map from a {@code CommandClass} to + * Constructs a new instance using the map from a {@code CommandClass} to * a {@code BoundedContext} instance which handles the command. */ private CommandService(Map map) { @@ -70,7 +75,9 @@ public static Builder newBuilder() { return new Builder(); } - /** Builds the service with a single Bounded Context. **/ + /** + * Builds the service with a single Bounded Context. + */ public static CommandService withSingle(BoundedContext context) { CommandService result = newBuilder() .add(context) @@ -78,6 +85,31 @@ public static CommandService withSingle(BoundedContext context) { return result; } + /** + * {@inheritDoc} + * + *

In the original proto definition of this service, this method is unary. + * Meaning, for every posted command only a single {@link Ack} is received in response: + * + *

Ack post(Command);
+ * + *

But for every service, gRPC generates several clients: blocking, asynchronous, + * {@link com.google.common.util.concurrent.ListenableFuture Future}-based. Each one + * has its own signature for this method. And instead of making users to implement three + * different signatures of the same method in the + * {@linkplain CommandServiceGrpc.CommandServiceImplBase server stub}, they prefer a single + * universal method that cover all the cases. + * + *

Although, utilizing of {@link StreamObserver} is quite controversial decision for the + * method which does not stream anything. It is still better than implementing this method + * three times. For more details on this matter, reference the issue below. + * + *

Please note, all the errors, occurring on the side of the application are still + * propagated through the {@linkplain Ack acknowledgement}. They are not passed into the + * {@linkplain StreamObserver#onError(Throwable) observer}. + * + *

See issue: Improve unary server stub + */ @Override public void post(Command request, StreamObserver responseObserver) { CommandClass commandClass = CommandClass.of(request);