Hey @imnowone, in order to achieve this behavior you will need to override two methods of the serializer. The first is resolveClassName to resolve a fully qualified classname back from the shorter identifier. The second is typeForClass, in order to get the shorter identifier when serializing the payload.
I don’t know how your requirements are exactly, but I hope this example will provide you with the inspiration needed:
public class ShorterJacksonSerializer extends JacksonSerializer {
protected ShorterJacksonSerializer(Builder builder) {
super(builder);
}
String basePackage = "com.my.base.package.which.i.want.to.leave.out";
protected String resolveClassName(SerializedType serializedType) {
return basePackage + "." + serializedType.getName();
}
@Override
public SerializedType typeForClass(Class type) {
if (type.getName().startsWith(basePackage)) {
String shortName = type.getName().replace(basePackage + ".", "");
return new SimpleSerializedType(shortName, getRevisionResolver().revisionOf(type));
}
throw new IllegalArgumentException("All payloads have to match base class " + basePackage);
}
}
In this example, the base package will be stripped when serializing to the database. When deserializing, it is added.
I do have to warn that this seems like a somewhat fragile approach to me. You will have to keep a mapping of class to fully qualified name, if you only want to save the classname. I would carefully consider if my cause warrants this, since there won’t be any performance and barely any storage cost gains.
Good luck on your implementation, let me know if there are any other questions!
that’s why i want to know how to implement custom payload type.
public class ShorterJacksonSerializer extends JacksonSerializer {
protected ShorterJacksonSerializer(Builder builder) {
super(builder);
}
String basePackage = "com.example.axon";
protected String resolveClassName(SerializedType serializedType) {
return basePackage + "." + serializedType.getName();
}
@Override
public SerializedType typeForClass(Class type) {
if (type.getName().startsWith(basePackage)) {
String shortName = type.getName().replace(basePackage + ".", "");
return new SimpleSerializedType(shortName, getRevisionResolver().revisionOf(type));
}
throw new IllegalArgumentException("All payloads have to match base class " + basePackage);
}
}
after using above code, i can see o.a.c.gateway.DefaultCommandGateway : Command 'com.example.axon.CreateSampleCommand' resulted in org.axonframework.eventsourcing.eventstore.EventStoreException(Cannot reuse aggregate identifier [SampleId(id=4_101_101)] to create aggregate [SampleAggregate] since identifiers need to be unique.) Exception.
Hi @imnewone , whether it’s right or wrong depends on the context you are using it. The approach you are trying here does come with some considerations though.
Take for example the following event: com.my.event.package.MyEvent. This is now saved to the event store as payload type MyEvent. The information of the package is lost, so you lose the ability to have an event by the same name, even across different contexts or applications.
It is also solving a problem you do not yet have, which is called premature optimization. You make extra effort to prevent a situation that might (or might not) happen down the road. It makes your application harder to understand while solving a problem that might not occur.
Personally, I would approach it by the framework default. Leave the package in the payload type, which is the most stable form of type definition you can have. If you are then required to move packages around later, you can always make a Serializer implementation that implements the resolveClassName and typeForClass that replaces the old with the new package and vice-versa.
This way you don’t need upcasters, and don’t need this premature optimization. You fix it when it’s needed. I think this is one of the situations where the YAGNI principle definitely applies.