Chuyển tới nội dung chính

5.13 — Review and Assessment

Mục tiêu bài học

  • Nắm được ý chính của bài và mối liên hệ với module.
  • Áp dụng được kiến thức vào bối cảnh CRM/.NET backend.
  • Sẵn sàng chuyển sang bài kế tiếp với nền tảng chắc chắn.

Nội dung bài học

5.13.1 — Thực hành

Bài 1 — Refactor CustomerService God Class

Yêu cầu: File CustomerService.cs hiện tại có 400 dòng gồm: CRUD, gửi email, tính loyalty point, export CSV, ghi audit log.

Refactor thành:

  • CustomerCrudService — chỉ CRUD
  • LoyaltyService — tính và cập nhật điểm
  • CustomerAuditService — ghi log
  • CustomerExportService — export CSV
  • Dùng event để trigger email thay vì gọi trực tiếp

Checklist:

  • Mỗi class không quá 80 dòng
  • Không class nào new() dependency trực tiếp
  • Có unit test cho từng class (mock dependency qua interface)

Bài 2 — LINQ Thống Kê CRM

Cho AppDbContext có: Customers, Leads, Deals. Viết các query sau:

// 1. Tổng doanh thu theo từng tháng trong năm hiện tại
var monthlyRevenue = await context.Deals
.Where(d => d.Status == DealStatus.Won && d.ClosedDate.Year == DateTime.Today.Year)
.GroupBy(d => d.ClosedDate.Month)
.Select(g => new MonthlyRevenueDto
{
Month = g.Key,
Revenue = g.Sum(d => d.Value),
Deals = g.Count()
})
.OrderBy(r => r.Month)
.ToListAsync();

// 2. Top 5 khách hàng theo tổng giá trị deal đã thắng
var top5 = await context.Customers
.Select(c => new
{
c.Name,
TotalRevenue = c.Deals
.Where(d => d.Status == DealStatus.Won)
.Sum(d => d.Value)
})
.OrderByDescending(c => c.TotalRevenue)
.Take(5)
.ToListAsync();

// 3. Lead conversion rate theo nguồn (source)
var conversionRate = await context.Leads
.GroupBy(l => l.Source)
.Select(g => new
{
Source = g.Key,
Total = g.Count(),
Converted = g.Count(l => l.Status == LeadStatus.Converted),
Rate = g.Count(l => l.Status == LeadStatus.Converted) * 100.0 / g.Count()
})
.OrderByDescending(r => r.Rate)
.ToListAsync();

Bài 3 — Generic PagedResult<T>Result<T> Error Handling

// Mở rộng EfRepository để trả PagedResult
public async Task<PagedResult<T>> GetPagedAsync(
ISpecification<T> spec, int page, int pageSize)
{
var query = _set.AsQueryable();

if (spec.Criteria is not null)
query = query.Where(spec.Criteria);

if (spec.OrderBy is not null)
query = query.OrderBy(spec.OrderBy);

var total = await query.CountAsync();
var items = await query.ApplyPaging(page, pageSize).ToListAsync();

return new PagedResult<T>(items, page, pageSize, total);
}

// Service dùng Result<T> để tránh exception-driven flow
public async Task<Result<PagedResult<LeadDto>>> GetLeadsAsync(LeadQuery query)
{
if (query.Page < 1)
return Result<PagedResult<LeadDto>>.Fail("Page phải >= 1");

var spec = new LeadSpecification(query);
var paged = await _repo.GetPagedAsync(spec, query.Page, query.PageSize);
var dtos = paged.Items.Select(LeadDto.From).ToList();

return Result<PagedResult<LeadDto>>.Ok(new PagedResult<LeadDto>(dtos, paged.Page, paged.PageSize, paged.TotalCount));
}

// Controller xử lý Result
[HttpGet]
public async Task<IActionResult> GetLeads([FromQuery] LeadQuery query)
{
var result = await _leadService.GetLeadsAsync(query);
return result.IsSuccess
? Ok(result.Value)
: BadRequest(new { error = result.Error });
}

5.13.2 — Tóm tắt

Chủ đềĐiểm mấu chốt
SOLID5 nguyên lý giúp code mở rộng không phá code cũ
GenericsRepository<T>, PagedResult<T>, Result<T> là pattern dùng xuyên suốt app
LINQDeferred execution — luôn .ToList() khi cần snapshot; ưu tiên method syntax
Delegates/EventsAction, Func cho callback; event pattern để decouple service
Extension MethodsĐặt trong *Extensions.cs, phải là static class, static method, this param đầu
Nullable Reference TypesBật <Nullable>enable</Nullable> ngay từ đầu project, dùng ? rõ ràng
Expression TreesDùng khi cần dynamic query; tránh Reflection trong hot path
TuplesTiện cho giá trị trả về nội bộ; dùng class/record khi có domain meaning

5.13.3 — Checklist trước khi sang bài tiếp theo

  • Bạn đã nắm được các khái niệm chính và giải thích lại được bằng ví dụ của riêng mình.
  • Bạn đã chạy hoặc mô phỏng ít nhất một ví dụ trong bài.
  • Bạn đã hoàn thành phần quiz/lab cơ bản (hoặc ít nhất tự làm lại theo trí nhớ).
  • Bạn đã ghi lại 3 ý chính: học được gì, còn vướng gì, sẽ áp dụng ở đâu trong CRM.

5.13.4 — Kiểm tra & Thực hành (100 điểm)

5.13.5 — Bối cảnh giáo trình (mục tiêu xuyên suốt)

Trục CRM — một mục tiêu rõ: bạn hướng tới CRM production trong From Zero → Senior .NET (Backend-first). Giáo trình ưu tiên backend, bám chuỗi API → dữ liệu → vận hành → kiến trúc phân tán, thay vì trải rộng theo hướng full-stack. Mỗi module củng cố một lớp kỹ năng trên cùng một CRM học tập bạn phát triển xuyên suốt khóa.

Chuỗi năng lựcĐóng góp vào CRM học tập
Giai đoạn 1 — FoundationĐọc hiểu yêu cầu, mô hình client–server, Git — nền collaboration khi CRM lớn dần.
Giai đoạn 2 — C# + P1Domain, OOP, LINQ, async, DI — rule nghiệp vụ & tầng ứng dụng (Inventory là bản mẫu trước CRM).
Giai đoạn 3 — ASP.NET + P2Host, routing, validation, JWT, SignalR — CRM Backend API thực tế.
Giai đoạn 4 — Data + P3SQL, EF, cache, job, container — CRM ổn định & có thể triển khai.
Giai đoạn 5 — Senior + FinalClean Architecture, event/outbox, gateway, observability, perf — CRM/ERP chịu tải & có chủ.

5.13.5.1 — Vai trò giai đoạn này trong chuỗi

C# professional + Project 1 (Inventory): OOP, LINQ, async, DI — domain & rule nghiệp vụ giống tầng lõi CRM/ERP.

5.13.5.2 — Vị trí bài học (trước / sau)

MốcĐiểm nối trong giáo trình
TrướcModule 4 — C# Core
Tiếp theoModule 6 — Async Programming

5.13.6 — Quy trình làm bài (hệ thống)

BướcViệc cần làmOutput nên có
1Làm Quiz trước, tự ghi đáp án + 1 dòng lý doKhông xem đáp án; giữ bản nháp
2Đối chiếu Đáp án; sửa hiểu sai, ghi takeaway vào README hoặc nhật ký học3–5 bullet “tôi đã hiểu…”
3Làm Lab theo rubric; trong README nêu phần CRM nào được chạm (Lead, Customer, Deal, Billing…)Repo / zip + ảnh dotnet run hoặc log
4Tự chấm theo bảng điểm; chuẩn bị 1 phút “vấn đáp” nếu mentor hỏi ngẫu nhiênĐiểm + chỗ còn yếu

5.13.7 — Phần A — Quiz trắc nghiệm (20 điểm)

Gợi ý làm bài: mỗi câu 4 điểm. Ưu tiên suy nghĩ hệ quả trên CRM (tiền, dữ liệu, đa tenant, hiệu năng, bảo mật) — không chỉ nhớ định nghĩa.

Mỗi câu 4 điểm.

  1. LINQ deferred execution nghĩa là?
    A. Query chạy ngay khi viết B. Query thực thi khi enumerate (.ToList(), foreach…) C. Luôn chạy trên GPU D. Không áp dụng cho IEnumerable

    Đáp án: B.

  2. Func<T, bool> trong Where là ví dụ của?
    A. Event B. Delegate C. Attribute D. Indexer

    Đáp án: B.

  3. Nullable Reference Types (<Nullable>enable) giúp?
    A. Biến null thành số B. Cảnh báo/reference analysis cho null an toàn hơn C. Tắt GC D. Thay thế async

    Đáp án: B.

  4. Extension method bắt buộc khai báo trong?
    A. static class với tham số đầu this T B. Instance class bất kỳ C. record sealed D. File .resx

    Đáp án: A.

  5. Expression tree khác lambda “thường” ở chỗ?
    A. Luôn nhanh hơn B. Có thể inspect/build runtime (IQueryable) C. Không compile D. Chỉ dùng trong WinForms

    Đáp án: B.

5.13.8 — Phần B — Lab thực tế (80 điểm)

Lab & rubric: trong README, map từng nhóm điểm rubric sang phần CRM (Lead, Customer, Deal, Billing, Notification…) và artifact (test, log, screenshot, migration).

Tiêu chíĐiểmGhi chú
10+ Customer/Lead giả: LINQ lọc, sắp, group (method syntax)30
Extension method thực tế (ví dụ IsVip(this Customer c))15
Event demo: OrderPlaced + 2 subscriber in log15
Bật Nullable Reference Types trong csproj, sửa hoặc giải thích warning chính (ảnh build)10
README + output mẫu10

5.13.8.1 — Ngưỡng tổng điểm (Quiz 20 + Lab 80)

MứcĐiểmÝ nghĩa
Đạt≥ 70Đủ nền để học module kế mà không “lủng” kiến thức cốt lõi.
Khá≥ 85Có minh chứng code + nêu được liên hệ CRM rõ ràng trong README.
Giỏi≥ 95Có mở rộng / edge case / ADR ngắn / test bổ trợ (tùy rubric từng bài).

5.13.9 — Phần C — Reflect & nối chuỗi (không chấm điểm)

Viết 5–8 câu (README hoặc docs/learning-log.md):

  1. Kiến thức module / project này sẽ được tái sử dụng trực tiếp ở module hoặc project CRM nào tiếp theo? (ghi tên module / P2 / P3 / Final).
  2. Nếu bỏ qua phần lab, rủi ro lớn nhất cho CRM ở giai đoạn sau là gì?
  3. Một quyết định kỹ thuật (nhỏ) bạn sẵn sàng ghi thành ADR một đoạn sau khi làm lab.

5.13.10 — Tài liệu tham khảo

Bài tập tổng hợp

  1. Tóm tắt bài học bằng ngôn ngữ của bạn.
  2. Liên hệ nội dung với một tình huống thực tế trong dự án.
  3. Đề xuất một cải tiến cụ thể sau khi học bài này.

Tự kiểm tra

  • Bạn có thể giải thích lại nội dung chính trong 2 phút không?
  • Bạn có ví dụ áp dụng thực tế chưa?
  • Bạn biết bước tiếp theo cần học/triển khai là gì không?

Kết luận

Hoàn thành bài này giúp bạn có góc nhìn đầy đủ hơn trước khi đi tiếp trong module.

Điều hướng