π§ Mastering 5 Design Patterns in C# - Parte 6


β¨ Introduction
These patterns focus on business logic composition, fault tolerance, and efficient processing. None of the previously listed patterns are repeated. Letβs dive into:
Specification
Event Sourcing
Throttling
Retry
Pipeline
π 1. Specification β Composable Business Rules
β Intent
Encapsulate business rules into reusable, combinable objects.
π― Use When
You need complex, reusable validation logic, such as deciding whether a notification should be sent.
π§© Code
public interface ISpecification<T>
{
bool IsSatisfiedBy(T item);
}
public class LengthSpecification : ISpecification<string>
{
public bool IsSatisfiedBy(string message) => message.Length <= 140;
}
public class NoSpamSpecification : ISpecification<string>
{
public bool IsSatisfiedBy(string message) => !message.Contains("spam");
}
public class AndSpecification<T> : ISpecification<T>
{
private readonly ISpecification<T> _a, _b;
public AndSpecification(ISpecification<T> a, ISpecification<T> b)
{
_a = a;
_b = b;
}
public bool IsSatisfiedBy(T item) => _a.IsSatisfiedBy(item) && _b.IsSatisfiedBy(item);
}
β Explanation
Each rule is encapsulated in its own class and can be combined using logical operators.
π Pros
Highly reusable and testable
Easy to compose complex rules
Promotes separation of concerns
π Cons
Can lead to many small classes
Slightly more verbose than inline logic
π§Ύ 2. Event Sourcing β Storing State as Events
β Intent
Store the state of a system as a sequence of events.
π― Use When
You want to track everything that happened, such as notifications sent, canceled, or retried.
π§© Code
public abstract class NotificationEvent
{
public DateTime Timestamp { get; } = DateTime.UtcNow;
}
public class NotificationSent : NotificationEvent
{
public string Message { get; }
public NotificationSent(string message)
{
Message = message;
}
}
public class EventStore
{
private readonly List<NotificationEvent> _events = new();
public void Add(NotificationEvent e) => _events.Add(e);
public IEnumerable<NotificationEvent> GetAll() => _events;
}
β Explanation
Events are stored instead of final state, allowing full reconstruction and auditability.
π Pros
Full history of changes
Great for auditing and debugging
Enables time travel and replay
π Cons
Requires more storage and management
Complex to query current state
π¦ 3. Throttling β Rate Limiting
β Intent
Limit the number of actions within a time window.
π― Use When
You want to prevent overload, such as limiting the number of notifications per minute.
π§© Code
public class Throttler
{
private readonly int _limit;
private readonly TimeSpan _interval;
private readonly Queue<DateTime> _timestamps = new();
public Throttler(int limit, TimeSpan interval)
{
_limit = limit;
_interval = interval;
}
public bool CanSend()
{
var now = DateTime.UtcNow;
while (_timestamps.Count > 0 && now - _timestamps.Peek() > _interval)
_timestamps.Dequeue();
if (_timestamps.Count < _limit)
{
_timestamps.Enqueue(now);
return true;
}
return false;
}
}
β Explanation
Keeps track of timestamps and enforces a limit within a time window.
π Pros
Prevents abuse or overload
Easy to implement
Useful for APIs and messaging systems
π Cons
Requires time tracking logic
May block legitimate requests
π 4. Retry β Fault-Tolerant Execution
β Intent
Retry a failed operation until it succeeds or reaches a limit.
π― Use When
You want to handle transient failures, such as network issues when sending notifications.
π§© Code
public class RetryPolicy
{
public static void Execute(Action action, int maxAttempts = 3)
{
int attempts = 0;
while (attempts < maxAttempts)
{
try
{
action();
return;
}
catch
{
attempts++;
Thread.Sleep(500); // wait between attempts
}
}
Console.WriteLine("Failed after multiple attempts.");
}
}
β Explanation
Wraps an action in retry logic with a delay between attempts.
π Pros
Improves resilience
Handles temporary failures gracefully
Simple to apply
π Cons
Can mask deeper issues
May cause delays or overload if abused
π§ͺ 5. Pipeline β Step-by-Step Processing
β Intent
Chain multiple processing steps in sequence.
π― Use When
You want to process notifications through multiple filters or transformations.
π§© Code
public interface IPipelineStep
{
string Process(string message);
}
public class TrimStep : IPipelineStep
{
public string Process(string message) => message.Trim();
}
public class UppercaseStep : IPipelineStep
{
public string Process(string message) => message.ToUpper();
}
public class NotificationPipeline
{
private readonly List<IPipelineStep> _steps = new();
public void AddStep(IPipelineStep step) => _steps.Add(step);
public string Execute(string message)
{
foreach (var step in _steps)
message = step.Process(message);
return message;
}
}
β Explanation
Each step transforms the message, allowing modular and reusable processing.
π Pros
Clean separation of concerns
Easy to extend or reorder steps
Great for validation, transformation, and enrichment
π Cons
Can become hard to debug if steps fail silently
Requires consistent step interfaces
π§Ύ Conclusion
These 5 patterns are especially useful in modern, distributed, or event-driven systems:
Specification enables flexible rule composition
Event Sourcing captures system history
Throttling controls frequency
Retry adds resilience
Pipeline organizes processing flow
#CSharp #DesignPatterns #DotNet #SoftwareEngineering #CleanArchitecture #BackendDev #CodeQuality #ProgrammingTips #SpecificationPattern #EventSourcingPattern #ThrottlingPattern #RetryPattern #PipelinePattern
Subscribe to my newsletter
Read articles from Johnny Hideki Kinoshita de Faria directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Johnny Hideki Kinoshita de Faria
Johnny Hideki Kinoshita de Faria
Technology professional with over 15 years of experience delivering innovative, scalable, and secure solutions β especially within the financial sector. I bring deep expertise in Oracle PL/SQL (9+ years), designing robust data architectures that ensure performance and reliability. On the back-end side, Iβve spent 6 years building enterprise-grade applications using .NET, applying best practices like TDD and clean code to deliver high-quality solutions. In addition to my backend strengths, I have 6 years of experience with PHP and JavaScript, allowing me to develop full-stack web applications that combine strong performance with intuitive user interfaces. I've led and contributed to projects involving digital account management, integration of VISA credit and debit transactions, modernization of payment systems, financial analysis tools, and fraud prevention strategies. Academically, I hold a postgraduate certificate in .NET Architecture and an MBA in IT Project Management, blending technical skill with business acumen. Over the past 6 years, Iβve also taken on leadership roles β managing teams, mentoring developers, and driving strategic initiatives. I'm fluent in agile methodologies and make consistent use of tools like Azure Boards to coordinate tasks and align team performance with delivery goals.