Complete Implementation
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json; // For serialization and deserialization
public abstract class MessageEnvelope<T>
{
// Immutable properties
public string EventType { get; private init; }
public string SourceService { get; private init; }
public DateTime Timestamp { get; private init; }
public Guid TraceId { get; private init; }
public T Payload { get; private init; }
// Private constructor to enforce the use of the builder
private MessageEnvelope() { }
// Static method to start building the envelope
public static Builder CreateBuilder() => new Builder();
// Nested Builder class
public class Builder
{
private readonly MessageEnvelope<T> _envelope = new ConcreteMessageEnvelope();
public Builder WithEventType(string eventType)
{
if (string.IsNullOrWhiteSpace(eventType))
throw new ArgumentException("EventType cannot be null or empty");
_envelope.EventType = eventType;
return this;
}
public Builder WithSourceService(string sourceService)
{
if (string.IsNullOrWhiteSpace(sourceService))
throw new ArgumentException("SourceService cannot be null or empty");
_envelope.SourceService = sourceService;
return this;
}
public Builder WithPayload(T payload)
{
_envelope.Payload = payload;
return this;
}
public Builder WithTimestamp(DateTime timestamp)
{
_envelope.Timestamp = timestamp;
return this;
}
public Builder WithTraceId(Guid traceId)
{
_envelope.TraceId = traceId;
return this;
}
public Builder WithoutPayload()
{
_envelope.Payload = default;
return this;
}
public MessageEnvelope<T> Build()
{
// Set defaults if not already set
_envelope.EventType ??= "Unknown";
_envelope.Timestamp = _envelope.Timestamp == default ? DateTime.UtcNow : _envelope.Timestamp;
_envelope.TraceId = _envelope.TraceId == default ? Guid.NewGuid() : _envelope.TraceId;
return _envelope;
}
}
// Clone method to replicate an envelope with modifications
public MessageEnvelope<T> Clone()
{
return CreateBuilder()
.WithEventType(this.EventType)
.WithSourceService(this.SourceService)
.WithPayload(this.Payload)
.WithTimestamp(this.Timestamp)
.WithTraceId(this.TraceId)
.Build();
}
// Serialization to JSON
public string ToJson()
{
return JsonConvert.SerializeObject(this);
}
// Deserialization from JSON
public static MessageEnvelope<T> FromJson(string json)
{
return JsonConvert.DeserializeObject<ConcreteMessageEnvelope>(json);
}
// Batch creation for multiple payloads
public static IEnumerable<MessageEnvelope<T>> CreateBatch(IEnumerable<T> payloads, string eventType, string sourceService)
{
return payloads.Select(payload =>
CreateBuilder()
.WithEventType(eventType)
.WithSourceService(sourceService)
.WithPayload(payload)
.Build());
}
// Example of a concrete implementation
private class ConcreteMessageEnvelope : MessageEnvelope<T>
{
}
}
Examples
1. Basic Envelope Creation
var envelope = MessageEnvelope<Reservation>
.CreateBuilder()
.WithEventType("ReservationExpiry")
.WithSourceService("ReservationService")
.WithPayload(new Reservation
{
ReservationId = "res-001",
SlotId = "slot-123",
ExpiryTime = DateTime.UtcNow
})
.Build();
Console.WriteLine(envelope.ToJson());
2. Creating an Envelope Without Payload
var metadataOnlyEnvelope = MessageEnvelope<object>
.CreateBuilder()
.WithEventType("SystemEvent")
.WithSourceService("MonitoringService")
.WithoutPayload()
.Build();
3. Cloning an Envelope
var clonedEnvelope = envelope.Clone();
Console.WriteLine(clonedEnvelope.ToJson());
4. Batch Creation
var reservations = new List<Reservation>
{
new Reservation { ReservationId = "res-001", SlotId = "slot-123", ExpiryTime = DateTime.UtcNow },
new Reservation { ReservationId = "res-002", SlotId = "slot-456", ExpiryTime = DateTime.UtcNow.AddHours(1) }
};
var envelopes = MessageEnvelope<Reservation>.CreateBatch(reservations, "ReservationExpiry", "ReservationService");
foreach (var env in envelopes)
{
Console.WriteLine(env.ToJson());
}
5. Serialization and Deserialization
string serialized = envelope.ToJson();
var deserialized = MessageEnvelope<Reservation>.FromJson(serialized);
Console.WriteLine($"Deserialized TraceId: {deserialized.TraceId}");
Conclusion
This implementation leverages Newtonsoft.Json
to serialize and deserialize objects efficiently. The inclusion of batch creation, cloning, and flexibility makes this envelope a robust solution for designing reliable messaging systems in distributed architectures.