Skip to content

Whats the suggested way to declare strongly typed events? (And why is CloudEvent sealed?) #308

@JoanComasFdz

Description

@JoanComasFdz

Hi all,

I have bee working in event-driven architectures using RabbitMQ for the last years, and the approach, for relatively small products, is that the project that owns an event is declaring the event in a separate project like (oversimplifying):

public record MyEvent(string topic, ..., MyPayload payload) : BaseEvent { ... }

A library receives the instance and serializes it all to publish it to RabbitMQ. And on the subscribing side, it uses the class definition to deserialize the string and pass it onto the subscription.

This allows all actors (publishers, subscribers, tests, etc) to use a single definition for a single event while freeing all the code from serialization, deserialization and casting.

I am looking to migrate this implementation to CloudEvents, so the first thing I thought about was:

public record BaseEvent<T> : CloudEvent
{
   // constructor...
   
   // json attribute
   public TPayload Payload {get:}
}

But CloudEvent is sealed :(

My first alternative is to declare my BaseEvent with operators to cast to and from CloudEvent:

using CloudNative.CloudEvents;
using System.Text.Json;

public abstract record BaseEvent<TPayload>
{
    public string Id { get; init; } = Guid.NewGuid().ToString();
    public string Type { get; init; }
    public Uri Source { get; init; }
    public string Subject { get; init; }
    public DateTimeOffset Time { get; init; } = DateTimeOffset.UtcNow;
    public TPayload Data { get; init; }

    protected BaseEvent(TPayload data, string source, string type, string subject = null)
    {
        Data = data;
        Source = new Uri(source);
        Type = type;
        Subject = subject;
    }

    public static implicit operator CloudEvent(BaseEvent<TPayload> baseEvent)
    {
        return new CloudEvent
        {
            Id = baseEvent.Id,
            Type = baseEvent.Type,
            Source = baseEvent.Source,
            Subject = baseEvent.Subject,
            Time = baseEvent.Time,
            Data = JsonSerializer.Serialize(baseEvent.Data),
            DataContentType = "application/json"
        };
    }

    public static implicit operator BaseEvent<TPayload>(CloudEvent cloudEvent)
    {
        var data = JsonSerializer.Deserialize<TPayload>(cloudEvent.Data.ToString());
        var baseEvent = Activator.CreateInstance(typeof(BaseEvent<TPayload>), data, cloudEvent.Source.ToString(), cloudEvent.Type, cloudEvent.Subject) as BaseEvent<TPayload>;
        baseEvent.Id = cloudEvent.Id;
        baseEvent.Time = cloudEvent.Time.Value;
        return baseEvent;
    }
}

But before I move forward, I am double checking:

  • What would be the proposed way to declare cloud events strongly typed?

An additionally:

  • Why is CloudEvent sealed?
  • Why is it a class and not a record?

Thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions