Customize event payload type

i use jackson serializer
event with pacakge names are inserted as payload type but i only want to insert event type

i saw PayloadType is with the full payloadType + Event class Name already

but im struggling to implement custom serializer
anyone help me with example code please?

class CustomSerializer(builder: Builder) : JacksonSerializer(builder) {

    override fun resolveClassName(serializedType: SerializedType): String {
        return serializedType.name
    }
}
return MongoEventStorageEngine
            .builder()
            .mongoTemplate(
                this.mongoTemplate(client)
            )
            .snapshotSerializer(CustomSerializer(JacksonSerializer.builder()))
            .eventSerializer(CustomSerializer(JacksonSerializer.builder()))

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!

@Morlack Thank you very much for your quick reply!!

I have seen that there may be problems with refactoring from Refactoring where events are (packaging-wise), what happens to events already in evenstore (MongoDB).

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.

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));
        } else {
           return SimpleSerializedType(type.name, revisionResolver.revisionOf(type))
        }
    }
}

but after using above code, i can see only EventClass payload.
so It works!! but I don’t understand why it works.

Could you explain it plaese?

thanks.

1 Like

is it wrong to use like this?

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.

I hope this clears things up for you. Good luck!

1 Like

@Morlack thank you to let me know!!

When refactoring is needed, i should change the package name to modify it using resolveClassName and typeForClass.

Thank you so much for your answer!