Skip to main content

2 posts tagged with "ABP Framework"

Unit of Work, modularity and the conventions ABP brings to line-of-business systems.

View All Tags

Unit of Work in .NET and ABP: SaveChangesAsync Is Not a Commit

· 10 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

EF Core's DbContext is already a Unit of Work: it collects changes in the change tracker and pushes them to the database inside one transaction when you call SaveChanges. Writing your own IUnitOfWork purely to call SaveChanges is therefore usually redundant. ABP is a different matter entirely: there, the Unit of Work is ambient, opens automatically per request, and SaveChangesAsync inside a UoW does not commit the transaction — only CompleteAsync does. Misreading that one point is the source of most "the data is there sometimes" bugs.

Unit of Work is one of the most frequently reimplemented patterns in the .NET world, and also the one most frequently reimplemented for no reason. This post splits into two parts: what you actually need with plain EF Core, and what ABP has already done for you that you should understand before touching it.

Building a Multi-Tenant CRM Platform from Nothing: 20 Months on the Job

· 30 min read
Nguyễn Huỳnh Minh Tiến
Middle Fullstack Developer @ Utop.vn
Summary

In January 2025 I was handed a product to initialise — the first time in my career — a multi-tenant CRM platform whose repository was completely empty when I took it on. For nearly its first year it lived in demo mode, shown to customers across many different industries, until late 2025 when a healthcare client signed and it moved into real delivery. Twenty months later it is 18 microservices on ABP Framework / .NET 9 and 13 Angular libraries, built by a team that grew to 25 people. This is not a technical article but a career story told chronologically, and it includes the export screen that forgot its permission checks, the two weeks of Angular work I had to revert, the three months mid-year when I was moved to a different loyalty project and committed nothing, and a migration so boring that nobody noticed it had happened. It also covers the EAV architecture that lets customers configure their own data fields from the portal, along with the months I spent wrestling with its performance and that of the dynamic filter sitting on top of it, the stretch where I mentored four interns for the first time, and how the way I write code shifted from typing everything by hand to ChatGPT, then Cursor, then Claude.

On 14 January 2025 I opened a completely empty repository with nothing in my head but a handful of very ordinary questions: what to name the solution, which folder goes where, how to split the database tables.

Until then I had always inherited codebases that were already running, where the work was reading, fixing and adding screens. This felt entirely different: there was nothing to copy, and whatever I typed would become the thing everyone else followed. It sounds impressive in the telling, but sitting in front of that empty repo I was genuinely a bit overwhelmed.

My commit that day was a single line, Init - Databases, projects, and there was nothing ceremonial about it.

Twenty months later that repo holds 18 microservices with 29,607 commits from 25 people. I am still there, and every so often I still open git log --reverse to look at those first few lines.

git log --reverse --format="%h %ad %an %s" --date=short | head -5

I am writing this to read again in a few years. The product belongs to the company, so I have anonymised all the proper nouns — the product name, the repo name, the client, the internal libraries — while leaving the tech stack intact, since that is the part I can tell.