Build A .NET 8 SMS Campaign Service
Hey there, fellow developers! Ever wanted to build your own SMS campaign service? Well, you're in luck! This guide will walk you through scaffolding a robust .NET 8 application, complete with a web UI, REST API, and all the bells and whistles you need for sending and managing SMS campaigns. We'll cover everything from database design and task scheduling to SMS provider integration and logging. Ready to dive in? Let's get started!
High-Level Overview
First, let's zoom out and get a bird's-eye view of what we're building. Our SMS campaign service will be a single ASP.NET Core 8 application. This means we'll have both a web UI (built with Razor/MVC) and a REST API (documented with Swagger) running under one roof. This architecture simplifies deployment and management.
We'll use a database to store all of our data. The service will support both SQL Server and SQLite, allowing you to choose the best option for your needs. You can switch between them using an environment variable. For backups and structured logging, we'll leverage LiteDB. It is a lightweight, embedded database that's perfect for these tasks.
To handle task scheduling and background jobs, we'll use Coravel, a simple yet powerful .NET library. For logging, we'll use Serilog, a popular structured logging library that can write to the console and rolling files. Finally, the service will use cookie-based authentication for security, with a fixed expiration time to keep things secure.
Our application is designed to be resilient and persistent. It will handle restarts gracefully, ensuring that SMS messages are not duplicated and that everything is saved.
Core Features and Requirements
- ASP.NET Core 8: The foundation of our application.
- Web UI + REST API: A combined interface for easy management.
- SQL Server/SQLite: Flexible database options.
- LiteDB: Backups and structured logging.
- Coravel: Task scheduling and queueing.
- Serilog: Structured logging.
- Cookie Auth: Secure user authentication.
- Resilience: Safe restarts and idempotent operations.
SMS Functional Requirements: Sending and Managing SMS Campaigns
Now, let's get into the nitty-gritty of what our SMS service will actually do. We'll need to handle single SMS messages as well as full-fledged campaigns.
Single SMS
From the web UI, users should be able to schedule and send a single SMS message. This could be sent immediately or at a future time. The UI will allow users to specify the recipient and the message content.
Campaigns
Campaigns will be the heart of our service. Users will be able to create both one-time and recurring campaigns. Recurring campaigns can be set up with intervals or even Cron expressions.
For campaign recipients, users will have two options: They can add recipients manually, providing a name and mobile number, or they can select recipients from a list of stored contacts. When adding manually, we'll also allow users to input optional fields such as sex, date of birth, and email.
To keep track of everything, the UI will include a dedicated page for each campaign. This page will display all the recipients, their phone numbers, the delivery status of each message, and timestamps for sent and delivery confirmation.
Phone Number Rules: Ensuring Proper Formatting
One of the most critical aspects of an SMS service is handling phone numbers correctly. We'll enforce the following rules to ensure that messages are sent successfully:
- Acceptable Input: Phone numbers must start with
69
and consist of exactly 10 digits. This format is specific to Greek mobile numbers without the country code. - Normalization: We'll normalize any input that includes
+30
or30
prefixes to the correct format. - API Format: When making the SMS API requests, we'll always prepend the
30
prefix to the number. Therefore, the final format will be3069XXXXXXXX
. - Validation: Invalid phone numbers will be rejected. Spaces, dashes, and any leading characters will be removed during the normalization process.
Remote SMS Provider Integration
Our service will integrate with a remote SMS provider. This will typically involve making HTTP requests to their API. The details will be specific to the provider we choose.
- Sending SMS: We'll send SMS messages using an HTTP GET request. The URL and query keys will be determined by the provider.
- Tracking: The provider will also provide an endpoint for tracking the delivery status of each message. We'll need to make GET requests to this endpoint, using the SMS ID provided by the provider.
- Credentials: API keys and other credentials will be stored securely in the
appsettings.json
file.
Schedulers: Automating the Process
We'll use schedulers to automate the sending and tracking of SMS messages.
- Send Scheduler: This scheduler will run periodically (e.g., every minute) to find any single SMS messages and campaign batches that are due to be sent. It will enqueue these sends using the Coravel queue and mark them as such.
- Delivery Tracker: This scheduler will run periodically (e.g., every hour) to poll the provider's tracking endpoint for all messages that haven't reached a terminal state (e.g., delivered, failed). It will update the statuses accordingly.
- Nightly Backup: Every night, we'll back up the relational tables (contacts, campaigns, recipients, sent logs) to LiteDB.
“Send Once” Guarantee: Ensuring No Duplicate Sends
To prevent duplicate SMS messages, we'll implement a