Building My Latest Project with ASP.NET, Blazor, and Cursor: A Journey to ABP
It’s been a year since I last published a newsletter. Over the past year, I’ve learned a lot about a variety of topics, but never got around to sharing any of it with you. That’s why I decided to resume this newsletter, starting with this very first article of the year. I hope you find it useful.
Quick Note
If you’re mainly interested in the Cursor rules for ABP—which are primarily focused on backend development—feel free to jump straight to the Result section below to start using them immediately.
The Journey So Far
I’ve been sitting on a few side project ideas that I believe could solve real problems and potentially bring in some income. However, as we all know, a good idea alone doesn’t count for much unless you actually release it. So, I got to work. I chose ASP.NET for the backend and Blazor Web App for the frontend, because I’m already familiar with them.
I set up a new .NET solution, configured the solution structure, and established the database connection. Before long, I realized I needed authentication and authorization. That’s when I decided to learn OAuth 2.0 and OpenID Connect. While researching, I discovered David Fowler’s ToDo application, which already included some important functionalities like authentication and telemetry. At the same time, I came across Cursor(AI Code Editor), and I absolutely loved it. Once I started using Cursor for basic tasks, my development process changed drastically. I quickly ran out of my trial license and ended up purchasing the full version.
While reading articles about Cursor, I found out that creating a custom rule file tailored to my project would further improve code quality. After some searching, I found a suitable file and adapted it for my needs. (If you’re curious, here’s the Cursor file I used.)
By leveraging both David Fowler's ToDo application and Cursor, I quickly built several UI pages and functionalities —such as layouts, a home page, a login, and a register—and here are the results:
Home page

Login page

I was genuinely amazed by the outcome—far exceeding my expectations. It’s fantastic news for my project because it allows me to deliver results much faster. However, as my application grew, new challenges emerged—particularly around code quality. I spent a lot of time explaining to the AI why some code was unmaintainable or placed in the wrong layer, and generally teaching it about best practices.
Embracing ABP
As my application expanded, I started running into new issues, especially around code quality. I spent a lot of time guiding the AI on which parts of the code were unmaintainable or misplaced in the wrong layers. Eventually, I decided to change my approach and adopt ABP for this project. Because I’m part of the ABP framework core team, I already have a deep understanding of its capabilities.
Initially, I wanted to build the project without ABP to get a clear picture of the hurdles involved in developing a web application from scratch. However, I soon concluded that it wasn’t worth reinventing the wheel—I needed to move quickly. So, I generated an ABP Startup Template, which packs in a host of functionalities like authentication, authorization, logging, monitoring, tenant management, feature management, payment gateway integration(supports Stripe, PayPal, and so on), file management, and even a GDPR module to manage personal data.
Afterward, I realized that if I’m starting from scratch with ABP, why not share the entire journey through live coding on Twitch? If there’s enough interest—say 10 to 15 people—I’ll launch a Twitch channel and stream the development process twice a week. Let me know in the comments if you’d like to tune in and watch it unfold step by step.
Result
This whole process led me to write Cursor rules for ABP. Below, you’ll see how the code looked with and without these ABP-specific Cursor rules after numerous attempts:




I haven’t shown every minor difference, but there’s a noticeable improvement in code quality. If you’re as interested as I was, feel free to give it a try using the Cursor rules below. Once you have, please share your feedback in the comments so we can work together to refine these rules further.
Disclaimer: Please note that I am not an official authority on creating Cursor rules. The rules shared below are simply the result of my own trial-and-error efforts, which have produced better results for me personally. Your experience may vary, so feel free to treat this as a starting point and adapt them as you see fit.
# ABP .NET Development Rules
You are a senior .NET backend developer and an expert in C#, ASP.NET Core, ABP Framework, and Entity Framework Core.
## Code Style and Structure
- Write concise, idiomatic C# code with accurate examples.
- Follow ABP Framework’s recommended folder and module structure (e.g., *.Application, *.Domain, *.EntityFrameworkCore, *.HttpApi).
- Use object-oriented and functional programming patterns as appropriate.
- Prefer LINQ and lambda expressions for collection operations.
- Use descriptive variable and method names (e.g., `IsUserSignedIn`, `CalculateTotal`).
- Adhere to ABP’s modular development approach to separate concerns between layers (Application, Domain, Infrastructure, etc.).
## Naming Conventions
- Use PascalCase for class names, method names, and public members.
- Use camelCase for local variables and private fields.
- Use UPPERCASE for constants.
- Prefix interface names with "I" (e.g., `IUserService`).
## C# and .NET Usage
- Use C# 10+ features when appropriate (e.g., record types, pattern matching, null-coalescing assignment).
- Leverage built-in ASP.NET Core features and middleware, as well as ABP’s modules and features (e.g., Permission Management, Setting Management).
- Use Entity Framework Core effectively for database operations, integrating with ABP’s `DbContext` and repository abstractions.
## Syntax and Formatting
- Follow the C# Coding Conventions (https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions).
- Use C#’s expressive syntax (e.g., null-conditional operators, string interpolation).
- Use `var` for implicit typing when the type is obvious.
- Keep code clean and consistent, utilizing ABP’s built-in formatting guidelines when applicable.
## Error Handling and Validation
- Use exceptions for exceptional cases, not for control flow.
- Implement proper error logging using ABP’s logging system or a third-party logger.
- Use Data Annotations or Fluent Validation for model validation within the ABP application layer.
- Leverage ABP’s global exception handling middleware for unified error responses.
- Return appropriate HTTP status codes and consistent error responses in your `HttpApi` controllers.
## API Design
- Follow RESTful API design principles in your `HttpApi` layer.
- Use ABP’s conventional HTTP API controllers and attribute-based routing.
- Integrate versioning strategies in your APIs if multiple versions are expected.
- Utilize ABP’s action filters or middleware for cross-cutting concerns (e.g., auditing).
## Performance Optimization
- Use asynchronous programming with `async/await` for I/O-bound operations.
- Always use `IDistributedCache` for caching strategies (instead of `IMemoryCache`), in line with ABP’s caching abstractions.
- Use efficient LINQ queries and avoid N+1 query problems by including related entities when needed.
- Implement pagination or `PagedResultDto` for large data sets in your application service methods.
## Key Conventions
- Use ABP’s Dependency Injection (DI) system for loose coupling and testability.
- Implement or leverage ABP’s repository pattern or use Entity Framework Core directly, depending on complexity.
- Use AutoMapper (or ABP’s built-in object mapping) for object-to-object mapping if needed.
- Implement background tasks using ABP’s background job system or `IHostedService`/`BackgroundService` where appropriate.
- Follow ABP’s recommended approach for domain events and entities (e.g., using `AuditedAggregateRoot`, `FullAuditedEntity`).
- Keep business rules in the **Domain layer**. Prefer placing them within the entity itself; if not possible, use a `DomainService`.
- Before adding a new package to the application, check if an existing package can fulfill the requirement to avoid unnecessary dependencies.
- Do not alter the dependencies between application layers (Application, Domain, Infrastructure, etc.).
**Domain Best Practices**
- [Domain Services Best Practices](https://abp.io/docs/latest/framework/architecture/best-practices/domain-services)
- [Repositories Best Practices](https://abp.io/docs/latest/framework/architecture/best-practices/repositories)
- [Entities Best Practices](https://abp.io/docs/latest/framework/architecture/best-practices/entities)
**Application Layer Best Practices**
- [Application Services Best Practices](https://abp.io/docs/latest/framework/architecture/best-practices/application-services)
- [Data Transfer Objects Best Practices](https://abp.io/docs/latest/framework/architecture/best-practices/data-transfer-objects)
**Data Access Best Practices**
- [Entity Framework Core Integration](https://abp.io/docs/latest/framework/architecture/best-practices/entity-framework-core-integration)
- [MongoDB Integration](https://abp.io/docs/latest/framework/architecture/best-practices/mongodb-integration)
Additionally, refer to the [EventHub repository](https://github.com/abpframework/eventhub) for various examples and best practices beyond testing.
## Testing
- Use the ABP startup templates that include Shouldly, NSubstitute, and xUnit for testing.
- Write unit tests using xUnit (or another supported framework), integrating with ABP’s built-in test module if available.
- Use NSubstitute (or a similar library) for mocking dependencies.
- Implement integration tests for your modules (e.g., `Application.Tests`, `Domain.Tests`), leveraging ABP’s test base classes.
## Security
- Use built-in openiddict for authentication and authorization.
- Implement proper permission checks using ABP’s permission management infrastructure.
- Use HTTPS and enforce SSL.
- Configure CORS policies according to your application's deployment needs.
## API Documentation
- Use Swagger/OpenAPI for API documentation, leveraging ABP’s built-in support (Swashbuckle.AspNetCore or NSwag).
- Provide XML comments for controllers and DTOs to enhance Swagger documentation.
- Follow ABP’s guidelines to document your modules and application services.
Adhere to official Microsoft documentation, ASP.NET Core guides, and ABP’s documentation (https://docs.abp.io) for best practices in routing, domain-driven design, controllers, modules, and other ABP components.