In Java, I would like to use hierarchies of immutable POJOs to express my domain model.
e.g.
final ServiceId id = new ServiceId(ServiceType.Foo, "my-foo-service")
final ServiceConfig cfg = new ServiceConfig("localhost", 8080, "abc", JvmConfig.DEFAULT)
final ServiceInfo info = new ServiceInfo(id, cfg)
All of these POJOs have public final fields with no getters or setters. (If you are a fan of getters, please pretend that the fields are private with getters.)
I would also like to serialize these objects using the MessagePack library in order to pass them around over the network, store them to ZooKeeper nodes, etc.
The problem is that MessagePack only supports serialization of public, non-final fields, so I cannot serialize the business objects as-is. Also MessagePack does not support (Yes it does, if you add an annotation to your enum
, so I have to convert enum values to int
or String
for serialization.enum
s. See my comment below.)
To deal with this I have a hand-written corresponding hierarchy of "message" objects, with conversions between each business object and its corresponding message object. Obviously this is not ideal because it causes a large amount of duplicated code, and human error could result in missing fields, etc.
Are there any better solutions to this problem?
- Code generation at compile time?
- Some way to generate the appropriate serializable classes at runtime?
- Give up on MessagePack?
- Give up on immutability and
enum
s in my business objects? - Is there some kind of generic wrapper library that can wrap a mutable object (the message object) into an immutable one (the business object)?
MessagePack also supports serialization of Java Beans (using the @MessagePackBeans annotation), so if I can automatically convert an immutable object to/from a Java Bean, that may get me closer to a solution.
It sounds like you have merged, rather than separated, the read and write concerns of your application. You should probably consider CQRS at this point.
In my experience, immutable domain objects are almost always attached to an audit story (requirement), or it's lookup data (enums).
Your domain should probably be, mostly, mutable, but you still don't need getters and setters. Instead you should have verbs on your objects which result in a modified domain model, and which raise events when something interesting happens in the domain (interesting to the business -- business == someone paying for your time). It's probably the events that you're interested in passing over the wire, not the domain objects. Maybe it's even the commands (these are similar to events, but the source is an agent external to the bounded context in which your domain lives -- events are internal to the model's bounded context).
You can have a service to persist the events (and another one to persist commands), which is also your audit-log (fulfilling your audit stories).
You can have an event handler that pushes your events onto your bus. These events should contain either simple information or entity ID's. The services that respond to these events should perform their duties using the information provided, or they should query for the information they need using the given ID's.
You really shouldn't be exposing the internal state of your domain model. You're breaking encapsulation by doing that, and that's not really a desirable thing to do. If I were you I'd take a look at the Axon Framework. It's likely to get you further than MessagePack alone.