MahdiHoseinpoor/EasyPay
EasyPay โ A modern .NET 9 financial platform API using CQRS, DDD, and Clean Architecture, with Blazor WebAssembly client.
EasyPay: Modern Financial Platform API
EasyPay is a showcase project demonstrating a robust, scalable, and maintainable financial application backend. It is built using modern .NET technologies and adheres strictly to Domain-Driven Design (DDD), CQRS, and Clean Architecture principles. This project serves as a testament to my expertise in building enterprise-grade systems with a focus on code quality and architectural integrity.
โจ Key Features
EasyPay is more than just a CRUD application; it's a feature-rich platform designed to handle core financial operations securely and efficiently.
๐ค User & Identity Management
- Secure Onboarding: Phone number-based registration with OTP verification (
NaturalUserRegisterPhoneCommand). - JWT Authentication: Stateless and secure authentication using JSON Web Tokens (
JwtTokenService). - Multi-Step Registration: A guided flow for new users from phone verification to profile completion (
CompleteNaturalUserProfileCommand). - Role-Based Access Control (RBAC): Granular permission management using ASP.NET Core Identity Roles and Claims. Features a custom MediatR pipeline behavior (
AuthorizationBehavior) to protect commands based on permissions likePermissions.Accounts.HardDelete. - Admin & User Roles: Clear separation between
SuperAdminandBasicUserroles.
๐ฆ Account Management
- Dynamic Account Creation: Users can create accounts based on predefined
AccountTypeentities. - Document-Driven Requirements: Account creation is dynamically gated by user-submitted and admin-approved documents (
CreateAccountCommandHandler). - Unique Account Number Generation: A robust service (
AccountNumberService) ensures no two accounts share the same number. - Admin-Managed Account Types: Admins can define different types of accounts, each with its own rules and document requirements.
๐ณ Financial Transactions
- Internal Transfers: Securely transfer funds between EasyPay accounts, with operations wrapped in EF Core transactions to ensure data integrity (
TransferMoneyCommandHandler). - External Deposits: Integrated with a payment gateway (Zarinpal) for funding accounts. The implementation uses a factory pattern (
IPaymentGatewayFactory) to support multiple gateways in the future. - Withdrawals: Users can withdraw funds to their saved bank cards.
- Comprehensive History: Paginated transaction history for user accounts (
GetMyTransactionHistoryQuery).
๐ก๏ธ KYC & Verification System
- Document Submission: Users can submit documents and information (
AuthItemValue) required for account verification. - Admin Verification Panel: Admins have a dedicated workflow to view, approve, or reject pending user submissions (
GetPendingSubmissionsQuery,ApproveAuthItemValueCommand).
๐๏ธ Architectural Overview
The project is built upon the principles of Clean Architecture, ensuring a clear separation of concerns, high cohesion, and low coupling between layers.
+-------------------+ +--------------------+ +----------------------+ +---------------------+
| Domain | <--- | Application | <--- | Infrastructure | <--- | Presentation |
| (Entities, VO) | | (CQRS, DTOs, | | (EF Core, | | (API Controllers, |
| | | Interfaces) | | Repositories) | | Blazor Client) |
+-------------------+ +--------------------+ +----------------------+ +---------------------+
Domain-Driven Design (DDD)
The core of the application is the EasyPay.Domain layer, which contains the business logic, free from any infrastructure concerns.
- Entities: Rich domain models like
Account,ApplicationUser,Transaction, andAuthItemValueencapsulate state and behavior. - Value Objects: Immutable objects like
TransactionMetadataensure data consistency and validity. - Repositories: Domain-centric data access interfaces are defined in
Infrastructureand used by theApplicationlayer.
CQRS (Command Query Responsibility Segregation)
The EasyPay.Application layer uses MediatR to implement the CQRS pattern, separating read operations (Queries) from write operations (Commands).
- Commands: Represent an intent to change the state of the system. Each command has a single, dedicated handler.
- Example:
TransferMoneyCommandis handled byTransferMoneyCommandHandler, which contains all the logic for validating and executing a fund transfer.
- Example:
- Queries: Represent an intent to read data from the system. Queries return DTOs (
EasyPay.Shared) to prevent leaking domain models to the UI.- Example:
GetMyAccountsQueryis handled byGetMyAccountsQueryHandlerto fetch a list of accounts for the current user.
- Example:
MediatR Pipeline Behaviors
To keep handlers clean and focused, cross-cutting concerns are handled gracefully in the MediatR pipeline:
ValidationBehavior<TRequest, TResponse>: Automatically validates incoming commands using FluentValidation before they reach the handler. This keeps validation logic separate and reusable.AuthorizationBehavior<TRequest, TResponse>: A custom behavior that checks if the authenticated user has the required permission to execute a command. This provides a declarative and powerful way to secure application features right at the source.
๐ป Technology Stack & Key Libraries
| Category | Technology / Library |
|---|---|
| Backend Framework | .NET 9, ASP.NET Core Web API |
| Architecture | MediatR, AutoMapper, FluentValidation |
| Data Access | Entity Framework Core 9 |
| Database | SQL Server |
| Authentication | ASP.NET Core Identity, JWT Bearer Tokens |
| API | RESTful Principles, API Versioning (Asp.Versioning.Mvc) |
| Frontend | Blazor WebAssembly |
| Testing | xUnit, Moq, FluentAssertions |
| Logging | Serilog |
| Error Handling | Custom Middleware, Custom Result<T> pattern |
๐ Code Quality & Best Practices
This project was written with a strong emphasis on clean, maintainable, and testable code.
- Result Pattern: Instead of throwing exceptions for predictable business rule failures (e.g., insufficient funds), handlers return a custom
Result<T>object. This makes error handling explicit and robust throughout the application stack, from the API controller down to the Blazor client. - Asynchronous Programming:
async/awaitis used extensively to ensure the application is scalable and responsive. - Dependency Injection: The solution is heavily reliant on DI, with services registered in
ApplicationandInfrastructurelayers for maximum decoupling. - Repository Pattern: A generic
RepositoryBase<TEntity, TKey>provides common data access logic, while specific repositories (IAccountRepository) extend it for domain-specific needs. - Unit Testing: The project includes a dedicated unit test project (
EasyPay.Application.UnitTests) that demonstrates how to test CQRS handlers in isolation using mocks. - Configuration Management: Utilizes the
IOptionspattern to inject strongly-typed configuration for JWT, storage, and payment gateway settings. - Global Exception Handling: A custom middleware (
GlobalExceptionHandlingMiddleware) catches any unhandled exceptions and returns a standardizedProblemDetailsresponse.
๐ Getting Started
Prerequisites
- .NET 9 SDK
- SQL Server (Express, Developer, or full version)
- A C# IDE like Visual Studio 2022 or VS Code.
Configuration
- Open
EasyPay/src/Presentation/EasyPay.Api/appsettings.Development.json. - Update the
DefaultConnectionStringto point to your SQL Server instance. - The
JwtSettingsand other configurations are pre-filled for local development.
Running the Application
- Run the API:
- Open the solution in Visual Studio.
- Set
EasyPay.Apias the startup project. - Press
F5or rundotnet runfrom theEasyPay.Apidirectory. - The database will be created and seeded with sample data on the first run.
- Run the Blazor Client:
- Set
EasyPay.Webas the startup project. - Press
F5or rundotnet runfrom theEasyPay.Webdirectory. - The Blazor application will open in your browser, connected to the local API.
- Set
A Note on the UI
As a dedicated backend developer, my primary focus for this project was on robust architecture, clean code, and powerful server-side functionality. To complement this, the user interface was crafted with the assistance of AI design tools, enabling me to present a functional and visually appealing Blazor WebAssembly client without diverting from my core expertise in the .NET ecosystem.
๐ Future Improvements
This project provides a solid foundation that can be extended in many ways:
- Develop Admin Panel: Create a new Blazor app specifically for administrators.
- Integration & End-to-End Testing: Add a test project to cover API endpoints and user flows.
- Implement Two-Factor Authentication (2FA): Enhance security for password-based logins.
- Expand Payment Gateway Support: Add more implementations to the
IPaymentGatewayFactory. - Real-time Notifications: Use SignalR to provide real-time updates for transaction statuses.
- Containerization: Add Docker support for easier deployment.
