Language-Specific Notes
Notable differences across Python, Go, Rust, Java, C#, and C++ implementations.
Quick Reference
Section titled “Quick Reference”| Concern | Python | Go | Rust |
|---|---|---|---|
| Error returns | raise CommandRejectedError() | Return (nil, err) | Result<T, Status> |
| State mutation | Mutable dataclasses | Pointer receivers | &mut self |
| Timestamp | Timestamp(seconds=...) | timestamppb.Now() | prost_types::Timestamp |
| Any packing | any.Pack(event) | anypb.MarshalFrom() | Any::from_msg() |
| Concern | Java | C# | C++ |
|---|---|---|---|
| Error returns | throw new CommandRejectedError() | throw new CommandRejectedError() | throw CommandRejectedError() |
| State mutation | Mutable POJOs | Mutable classes | std::shared_ptr<State> |
| Timestamp | Timestamps.fromMillis() | Timestamp.FromDateTime() | google::protobuf::Timestamp |
| Any packing | Any.pack(event) | Any.Pack(event) | event.PackTo(&any) |
Python
Section titled “Python”Protobuf Handling
Section titled “Protobuf Handling”from google.protobuf.any_pb2 import Anyfrom google.protobuf.timestamp_pb2 import Timestamp
# Packing eventsevent_any = Any()event_any.Pack(event, type_url_prefix="type.googleapis.com/")
# Unpacking eventsif event_any.Is(PlayerRegistered.DESCRIPTOR): event = PlayerRegistered() event_any.Unpack(event)
# Timestampsimport timetimestamp = Timestamp(seconds=int(time.time()))Gotchas
Section titled “Gotchas”- Protobuf messages are mutable—avoid accidental mutation
- Use
Timestamp(seconds=int(...))not datetime objects - gRPC status codes via
grpc.StatusCode.FAILED_PRECONDITION
Protobuf Handling
Section titled “Protobuf Handling”import ( "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb")
// Packing eventseventAny, err := anypb.New(event)
// Unpacking eventsvar event PlayerRegisteredif err := eventAny.UnmarshalTo(&event); err != nil { // handle error}
// Timestampstimestamp := timestamppb.Now()Gotchas
Section titled “Gotchas”- Return tuple
(result, error)—never panic - Use
status.Error(codes.X, msg)for gRPC errors - Protobuf
timestamppb.Now()for timestamps
Protobuf Handling
Section titled “Protobuf Handling”use prost_types::{Any, Timestamp};use std::time::SystemTime;
// Packing eventslet event_any = Any::from_msg(&event)?;
// Unpacking eventslet event: PlayerRegistered = event_any.to_msg()?;
// Timestampslet timestamp = Timestamp::from(SystemTime::now());Gotchas
Section titled “Gotchas”Stringvs&str—proto fields areString, clone if neededOption<T>for optional proto fields (timestamps, nested messages)- Use
Result<T, Status>for error handling prost_types::Any::from_msg()for packing
Protobuf Handling
Section titled “Protobuf Handling”import com.google.protobuf.Any;import com.google.protobuf.Timestamp;import com.google.protobuf.util.Timestamps;
// Packing eventsAny eventAny = Any.pack(event);
// Unpacking eventsif (eventAny.is(PlayerRegistered.class)) { PlayerRegistered event = eventAny.unpack(PlayerRegistered.class);}
// TimestampsTimestamp timestamp = Timestamps.fromMillis(System.currentTimeMillis());Gotchas
Section titled “Gotchas”- Proto messages are immutable—use builders
- Use
StatusRuntimeExceptionfor gRPC errors - Check
is()beforeunpack()to avoid exceptions
Protobuf Handling
Section titled “Protobuf Handling”using Google.Protobuf.WellKnownTypes;
// Packing eventsvar eventAny = Any.Pack(evt);
// Unpacking eventsif (eventAny.Is(PlayerRegistered.Descriptor)){ var evt = eventAny.Unpack<PlayerRegistered>();}
// Timestampsvar timestamp = Timestamp.FromDateTime(DateTime.UtcNow);Gotchas
Section titled “Gotchas”- Proto messages are mutable in C# (unlike Java)
- Use
RpcExceptionfor gRPC errors - Always use UTC for timestamps
Protobuf Handling
Section titled “Protobuf Handling”#include <google/protobuf/any.pb.h>#include <google/protobuf/timestamp.pb.h>
// Packing eventsgoogle::protobuf::Any event_any;event_any.PackFrom(event);
// Unpacking eventsPlayerRegistered event;if (event_any.Is<PlayerRegistered>()) { event_any.UnpackTo(&event);}
// Timestampsgoogle::protobuf::Timestamp timestamp;timestamp.set_seconds(std::time(nullptr));Gotchas
Section titled “Gotchas”- Memory management—use smart pointers
- Check
Is<T>()beforeUnpackTo() - gRPC status via
grpc::Status(grpc::StatusCode::X, msg)
Error Handling Comparison
Section titled “Error Handling Comparison”All languages use the same OO @handles / @applies pattern, but error propagation differs:
Python: raise CommandRejectedError(errmsg.PLAYER_NOT_REGISTERED)Go: return nil, status.Error(codes.FailedPrecondition, errmsg.PlayerNotRegistered)Rust: return Err(Status::failed_precondition(errmsg::PLAYER_NOT_REGISTERED))Java: throw new CommandRejectedError(ErrMsg.PLAYER_NOT_REGISTERED)C#: throw new CommandRejectedError(ErrMsg.PlayerNotRegistered)C++: throw CommandRejectedError(errmsg::PLAYER_NOT_REGISTERED)The error messages are identical across languages—only the propagation mechanism differs.
Next Steps
Section titled “Next Steps”- Aggregates — Full handler examples
- Testing — Cross-language Gherkin tests