N-tier architecture in .NET: a layered BLL/DAL example in C# and its problems
Layer = logical separation in the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // ====================================================================== // Q: Show me an N-tier application in .NET. What's the problem with it? // // Layer = logical separation in the code. Tier = physical separation (a // different process or server). The classic 3-tier setup: browser, application // server, database server. In practice, "N-tier" usually means the layers. // - Problem 1: the business logic depends on the DAL. Changing the database or // ORM affects the BLL. // - Problem 2: unit testing OrderManager without a database is hard (concrete // repository). // - Problem 3: anemic entities: the rules are scattered across managers, and // Status is changed from everywhere. // - Advantage: simple, everyone understands it. For CRUD without special rules // it is enough. // ====================================================================== // Classic N-tier layering: Web -> BLL -> DAL Shop.Web → Shop.BLL → Shop.DAL → SQL (controllers) (managers) (DbContext, entities, repositories) // DAL: EF entities and repositories public class OrderRepository(ShopDbContext db) { public Task<OrderEntity?> GetAsync(int id) => db.Orders.Include(o => o.Lines).FirstOrDefaultAsync(o => o.Id == id); public Task SaveAsync() => db.SaveChangesAsync(); } // BLL: depends directly on the DAL and the EF entities public class OrderManager(OrderRepository repo) { public async Task CancelAsync(int id) { var order = await repo.GetAsync(id) ?? throw new NotFoundException(); if (order.Status == "Shipped") throw new BusinessException("Already shipped"); order.Status = "Cancelled"; // anemic entity: anyone can change it await repo.SaveAsync(); } } // Presentation [HttpPost("{id}/cancel")] public Task Cancel(int id) => orderManager.CancelAsync(id); |