Complete Implementation of a Message Envelope Using Newtonsoft.Json

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. ...

March 15, 2025 · 3 min · Taner

Updated Implementation Using `System.Text.Json`

Updated Implementation Using System.Text.Json using System; using System.Text.Json; using System.Text.Json.Serialization; public abstract class MessageEnvelope<T> { // Properties remain immutable 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 for builder use only private MessageEnvelope() { } public static Builder CreateBuilder() => new Builder(); // Serialization to JSON public string ToJson() { var options = new JsonSerializerOptions { WriteIndented = true // Makes the output JSON easier to read }; return JsonSerializer.Serialize(this, options); } // Deserialization from JSON public static MessageEnvelope<T> FromJson(string json) { var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true // Handles case differences in JSON properties }; return JsonSerializer.Deserialize<ConcreteMessageEnvelope>(json, options); } // Concrete implementation example for proper deserialization private class ConcreteMessageEnvelope : MessageEnvelope<T> { } public class Builder { private readonly MessageEnvelope<T> _envelope = new ConcreteMessageEnvelope(); public Builder WithEventType(string eventType) { _envelope.EventType = eventType; return this; } public Builder WithSourceService(string sourceService) { _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 MessageEnvelope<T> Build() { _envelope.Timestamp = _envelope.Timestamp == default ? DateTime.UtcNow : _envelope.Timestamp; _envelope.TraceId = _envelope.TraceId == default ? Guid.NewGuid() : _envelope.TraceId; return _envelope; } } } Example Usage 1. Serialize a MessageEnvelope to JSON var envelope = MessageEnvelope<Reservation> .CreateBuilder() .WithEventType("ReservationExpiry") .WithSourceService("ReservationService") .WithPayload(new Reservation { ReservationId = "res-001", SlotId = "slot-123", ExpiryTime = DateTime.UtcNow }) .Build(); string serialized = envelope.ToJson(); Console.WriteLine($"Serialized Envelope:\n{serialized}"); 2. Deserialize a JSON String to MessageEnvelope string json = "{\"EventType\":\"ReservationExpiry\",\"SourceService\":\"ReservationService\",\"Timestamp\":\"2025-03-25T17:00:00Z\",\"TraceId\":\"8a1db2c2-ec3e-45f7-a3eb-bd9dfb351245\",\"Payload\":{\"ReservationId\":\"res-001\",\"SlotId\":\"slot-123\",\"ExpiryTime\":\"2025-03-25T17:00:00Z\"}}"; var deserializedEnvelope = MessageEnvelope<Reservation>.FromJson(json); Console.WriteLine($"Deserialized EventType: {deserializedEnvelope.EventType}"); Console.WriteLine($"Deserialized Reservation ID: {deserializedEnvelope.Payload.ReservationId}"); Benefits of Serialization Portability: You can transmit envelopes as JSON over APIs, message brokers, or store them in databases. Interoperability: Many systems can parse JSON, making serialized envelopes easy to integrate across platforms. Flexibility: Deserialization lets you reconstruct envelopes when receiving messages. Advantages of System.Text.Json Performance: Faster than Newtonsoft.Json, especially for large-scale applications. Built-in Support: No need for external dependencies; it’s natively part of .NET Core and .NET 5+. Configuration Options: Flexible JSON options like camel casing, case insensitivity, and indented formatting.

March 15, 2025 · 2 min · Taner