Using the Factory Design Pattern in a .NET 8 API

Table Of Contents
Introduction
In software development, one of the most critical design principles is ensuring that code is easy to maintain, extend, and test. The Factory Design Pattern is a widely used creational pattern that provides a way to create objects while hiding the creation logic from the client.
In this post, we’ll dive into the Factory Design Pattern, explain its importance, and demonstrate how to implement it in a .NET 8 API using a real-world example.
What is the Factory Design Pattern?
The Factory Design Pattern is a creational pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. It’s like a factory where you provide an input (the product type), and the factory determines how to create the correct product based on that input.
How the Factory Design Pattern Works
- Abstraction of Object Creation: The factory method hides the details of object creation.
- Dynamic Object Creation: The pattern allows creating objects dynamically, based on the input provided.
- Single Responsibility: The responsibility of the factory is to handle object creation, which adheres to the Single Responsibility Principle.
Benefits of the Factory Design Pattern
- Decoupling: It decouples the client from the implementation of the object creation.
- Extensibility: Adding new classes or objects is easy without changing the client code.
- Single Responsibility: The creation of objects is centralized, and the client does not need to know the details.
Now, let’s implement the Factory Design Pattern step by step in a .NET 8 API.
Step-by-Step Example: Notification API with Factory Design Pattern
Imagine we’re building an API where we need to handle notifications. The notifications can be sent via different channels like Email, SMS, or Push Notifications, each having its implementation. Using the Factory Design Pattern, we can dynamically create the appropriate notification handler.
Step 1: Define the INotification Interface
The first step is to define an interface that all notification types will implement:
public interface INotification{void Send(string to, string message);}
This interface ensures that every notification type (email, SMS, push) has a Send method.
Step 2: Implement Concrete Notification Classes
Now, we create concrete classes for each notification type:
public class EmailNotification : INotification{public void Send(string to, string message){Console.WriteLine($"Sending Email to {to}: {message}");}}public class SmsNotification : INotification{public void Send(string to, string message){Console.WriteLine($"Sending SMS to {to}: {message}");}}public class PushNotification : INotification{public void Send(string to, string message){Console.WriteLine($"Sending Push Notification to {to}: {message}");}}
Step 3: Create the NotificationFactory
The NotificationFactory class is responsible for determining which notification handler to use based on the input type:
public class NotificationFactory{public INotification CreateNotification(string type){return type.ToLower() switch{"email" => new EmailNotification(),"sms" => new SmsNotification(),"push" => new PushNotification(),_ => throw new InvalidOperationException("Invalid notification type")};}}
This factory will return the appropriate notification object based on the input.
Step 4: Add a Controller to the API
Now, let’s add a controller to handle the incoming requests. The controller uses the NotificationFactory to send notifications.
[ApiController][Route("api/[controller]")]public class NotificationController : ControllerBase{private readonly NotificationFactory _notificationFactory;public NotificationController(){_notificationFactory = new NotificationFactory();}[HttpPost]public IActionResult SendNotification([FromBody] NotificationRequest request){var notification = _notificationFactory.CreateNotification(request.Type);notification.Send(request.To, request.Message);return Ok("Notification sent");}}public class NotificationRequest{public string Type { get; set; } // Email, SMS, Pushpublic string To { get; set; }public string Message { get; set; }}
Step 5: Define the Request Model
The NotificationRequest class is a simple model that contains the notification type, the recipient (To), and the message:
public class NotificationRequest{public string Type { get; set; } // Can be "email", "sms", "push"public string To { get; set; }public string Message { get; set; }}
Step 6: Add Dependency Injection (Optional)
You can refactor the controller to use Dependency Injection (DI) for injecting the factory.
In Program.cs:
builder.Services.AddSingleton<NotificationFactory>();
Modify the controller to accept the factory through the constructor:
public NotificationController(NotificationFactory notificationFactory){_notificationFactory = notificationFactory;}
Step 7: Testing the API
To test the API, you can send a POST request to /api/Notification with the following JSON body to test different notification types.
For an email notification:
{"type": "email","to": "user@example.com","message": "Hello via Email!"}
For SMS:
{"type": "sms","to": "+1234567890","message": "Hello via SMS!"}
For Push Notification:
{"type": "push","to": "device_token","message": "Hello via Push Notification!"}
Conclusion
In this post, we implemented the Factory Design Pattern in a .NET 8 API. We learned how the factory pattern works and how it provides an abstraction for object creation. We also saw how this pattern enhances maintainability and extensibility by decoupling the object creation logic from the client code.
The Factory Pattern is a powerful design pattern that is particularly useful when you need to manage object creation dynamically and need the flexibility to introduce new types easily. This makes it a valuable tool for building scalable, maintainable APIs.
Feedback and Questions
Your insights drive us! For any questions, feedback, or thoughts, feel free to connect:
- Email: shady@shadynagy.com
- Twitter: @ShadyNagy_
- LinkedIn: Shady Nagy
- GitHub: ShadyNagy
If you found this guide beneficial, don’t hesitate to share it with your network. Until the next guide, happy coding!







