Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
149903 stories
·
33 followers

Tips from a Serial Career Changer with GitHub's Andrea Griffiths [Podcast #199]

1 Share

Today Quincy Larson interviews Andrea Griffiths, who taught herself programming using freeCodeCamp while working in construction. She moved to the US from Colombia when she was 17, and within 6 months she joined the US Army. She ran a chain of gyms before landing a support role at a tech company, then ascending to Product Manager and ultimately Developer Advocate at GitHub.

We talk about:

  • Tips for busy parents who want to learn new skills.

  • How AI tools are no substitute for your own critical thinking and problem solving skills.

  • How even though it's getting easier every day to learn programming for free, people are so distracted, and for many it feels harder and harder to sit down and do it.

Support for this podcast is provided by a grant from AlgoMonster. AlgoMonster is a platform that teaches data structure and algorithm patterns in a structured sequence, so you can approach technical interview questions more systematically. Their curriculum covers patterns like sliding window, two-pointers, graph search, and dynamic programming, helping you learn each pattern once and apply it to solve many problems. Start a structured interview prep routine at https://algo.monster/freecodecamp

Support also comes from the 10,338 kind folks who donate to our charity each month. Join them and support our mission at https://donate.freecodecamp.org

Get a freeCodeCamp t-shirt for $20 with free shipping anywhere in the US: https://shop.freecodecamp.org

Watch the podcast on the freeCodeCamp.org YouTube channel or listen on your favorite podcast app.

Links from our discussion:

Community news section:

1. freeCodeCamp just published this beginner-friendly back-end development course. You'll learn how to build your own web servers and APIs using Node.js, Express, and MongoDB. freeCodeCamp's website and mobile apps are built using these tools, which make up the popular MERN stack. You'll also get some exposure to database architecture, security principles, testing best practices, and more. (2 hour YouTube course): https://www.freecodecamp.org/news/intro-to-backend-web-development-nodejs-express-mongodb/

2. freeCodeCamp also published a comprehensive Blender and Three.js course where you'll build your own 3D portfolio piece: a render of an adorable home office. If you're interested in 3D rendering and computer graphics, this is the course for you. You'll learn key concepts like Quad Topology, Raycasting, OrbitControls, and more. By the end of the course, your 3D model will be live on the web so you can share it with your friends. (9 hour YouTube course): https://www.freecodecamp.org/news/create-a-cute-room-portfolio-with-threejs-blender-javascript/

3. freeCodeCamp also published a handbook on using Docker with Node.js. You'll learn how to set up Docker and Docker Compose. You'll also learn fundamental concepts like Volumes, Images, and Containers. This is an excellent resource for you to read through and code along with. Bookmark it for future reference. (full length handbook): https://www.freecodecamp.org/news/how-to-use-to-docker-with-nodejs-handbook/

4. Level up your JavaScript implementation skills with this new freeCodeCamp course on Clean Code. You'll learn how to detect “code smells” and refactor your JavaScript accordingly. You'll also learn how to use ESLint and Prettier to automate some of the more error-prone aspects of shipping code. (1 hour watch): https://www.freecodecamp.org/news/level-up-your-javascript-detect-smells-and-write-clean-code/

5. Classic text adventure games Zork I, II, and III are now open source with an MIT license. Microsoft has published their full source code on GitHub: https://github.com/historicalsource/zork1

6. Today's song of the week is 1985 classic "Something About You" by Level 42. I love the slap bass, the vocal harmony, the falsetto, and the huge synth sounds. It's impossible to listen to this song and still be in a bad mood: https://www.youtube.com/watch?v=zpdQQoc-gkk



Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

What is Jenkins Pipeline? Tutorial & Examples

1 Share
Learn how Jenkins Pipeline automates CI/CD workflows using code. Explore syntax, stages, steps, and best practices for fast delivery.
Read the whole story
alvinashcraft
26 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Build AI Apps with MCP Servers With DeepLearning.AI

1 Share
Read the whole story
alvinashcraft
32 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

How can I have a Win32 drop-down combo box with a read-only edit control?

1 Share

A customer was writing a Win32 program and was looking for something like a combo box, in that it gave the user a choice among various fixed options, but sometimes the program would come up with a new option that wasn’t on the list, and it would want to show that as the selection. On the other hand, the customer didn’t want to give the user the option to enter arbitrary text. Users still had to choose among the fixed items in the combo box or the new option that was generated on the fly. The customer didn’t want to add the new option to the dropdown list, which means that the CBS_DROP­DOWN­LIST style would not be appropriate, since that forces the selection to come from the list. What they wanted was for the combo box to act like a traditional combo box with edit control, except that the user can’t change the text in the edit control.

The solution here is to make the edit control read-only.

You can get the edit control handle by calling Get­Combo­Box­Info and then sending the EM_SET­READ­ONLY message to the window identified by the hwndItem member.

case WM_INITDIALOG:
{
    HWND combo = GetDlgItem(hdlg, IDC_COMBO);
    SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)L"Fixed item 1");
    SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)L"Fixed item 2");
    SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)L"Fixed item 3");

    COMBOBOXINFO info = { sizeof(info) };               
    GetComboBoxInfo(combo, &info);                      
    SendMessage(info.hwndItem, EM_SETREADONLY, TRUE, 0);
}
return TRUE;

The Get­Combo­Box­Info function is admittedly a bit hard to find because it’s not a message or window style, so it’s not in a place that you naturally look when trying to find information about a combo box.

The post How can I have a Win32 drop-down combo box with a read-only edit control? appeared first on The Old New Thing.

Read the whole story
alvinashcraft
40 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Stop Letting Your Controllers Talk to SQL: Layered Architecture in ASP.NET Core

1 Share

Walk into almost any long-lived enterprise codebase, and you will find the same pattern:

  • Controllers that know about routing, JSON, SQL, and domain rules
  • Repositories that reach up into HttpContext
  • Business rules scattered across UI, stored procedures, and helper classes

At that point, adding a new feature feels like surgery without a map. You poke at one place, something bleeds somewhere else, and nobody is sure why.

Layered architecture exists to stop that.

In this post, we will walk through a practical version of Fowler’s layered architecture in ASP.NET Core and C#. You will see:

  • What each layer is allowed to know
  • How to wire up a fundamental feature using three layers
  • How does this structure make change cheaper and failure less chaotic

The example centers on a simple use case: creating an order.

The core idea: three layers, three distinct responsibilities

Fowler’s baseline looks like this:

  1. Presentation layer
    Handles input and output. In web apps, this means HTTP, routing, model binding, and formatting responses.
  2. Domain layer
    Holds business rules, domain services, and aggregates. It talks about orders, customers, payments, not controllers or DbContext.
  3. Data source layer
    Owns persistence. It uses EF Core, raw SQL, caching, and talks to any external data store.

A simple rule captures the intent:

If your controllers know SQL or your repositories know HTTP, you already lost separation.

The rest of this post shows what it looks like when you refuse to cross those lines.

The scenario: placing an order in three slices

We will build a feature that lets a client create an order.

Requirements:

  • Clients call POST /orders with customer and line items
  • An order must contain at least one line item
  • The system persists the order in a relational database

We will implement that with:

  • A presentation layer endpoint
  • A domain service and aggregate
  • A data source layer using EF Core

Presentation layer: thin HTTP endpoint

The presentation layer should only:

  • Accept input
  • Call the domain layer
  • Shape the HTTP response

It should not:

  • Reach into DbContext
  • Perform business rules beyond basic request validation
  • Use EF Core directly

Example with ASP.NET Core minimal APIs:

// Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IOrderRepository, EfCoreOrderRepository>();

var app = builder.Build();

app.MapPost("/orders", async (CreateOrderDto dto, IOrderService orderService) =>
{
    // Basic request-level validation only
    if (dto.Lines is null || dto.Lines.Count == 0)
    {
        return Results.BadRequest("Order must contain at least one line item.");
    }

    var orderId = await orderService.CreateOrderAsync(dto.CustomerId, dto.Lines);
    return Results.Created($"/orders/{orderId}", new { Id = orderId });
});

app.Run();

public record CreateOrderDto(Guid CustomerId, List<OrderLineDto> Lines);

public record OrderLineDto(Guid ProductId, int Quantity);

Notice what the endpoint does not do:

  • It does not call AppDbContext
  • It does not calculate totals
  • It does not decide how orders are stored

It simply coordinates HTTP and the domain service.

Domain layer: business rules and language of the problem

The domain layer decides what an order is allowed to do.

It should:

  • Enforce invariants
  • Capture domain rules in one place
  • Express intent through the language of the business

It should not:

  • Know about HTTP
  • Know about EF Core
  • Reference ASP.NET Core packages

Domain service interface:

public interface IOrderService
{
    Task<Guid> CreateOrderAsync(
        Guid customerId,
        IReadOnlyCollection<OrderLineDto> lines);
}

Domain service implementation:

public class OrderService : IOrderService
{
    private readonly IOrderRepository _orders;

    public OrderService(IOrderRepository orders)
    {
        _orders = orders;
    }

    public async Task<Guid> CreateOrderAsync(
        Guid customerId,
        IReadOnlyCollection<OrderLineDto> lines)
    {
        if (!lines.Any())
        {
            throw new InvalidOperationException("Order must contain at least one line item.");
        }

        var order = Order.Create(
            customerId,
            lines.Select(l => new OrderLine(l.ProductId, l.Quantity)).ToList());

        await _orders.AddAsync(order);
        return order.Id;
    }
}

Domain model for Order and OrderLine:

public class Order
{
    private readonly List<OrderLine> _lines = new();

    private Order(Guid customerId)
    {
        Id = Guid.NewGuid();
        CustomerId = customerId;
        Status = OrderStatus.Draft;
        CreatedAt = DateTime.UtcNow;
    }

    public Guid Id { get; }
    public Guid CustomerId { get; }
    public OrderStatus Status { get; private set; }
    public DateTime CreatedAt { get; }
    public IReadOnlyCollection<OrderLine> Lines => _lines.AsReadOnly();

    public decimal TotalAmount => _lines.Sum(l => l.Total);

    public static Order Create(Guid customerId, IEnumerable<OrderLine> lines)
    {
        var order = new Order(customerId);

        foreach (var line in lines)
        {
            order.AddLine(line.ProductId, line.Quantity, line.UnitPrice);
        }

        if (!order._lines.Any())
        {
            throw new InvalidOperationException("Order must have at least one line item.");
        }

        return order;
    }

    public void AddLine(Guid productId, int quantity, decimal unitPrice)
    {
        if (Status != OrderStatus.Draft)
        {
            throw new InvalidOperationException("Cannot modify a non draft order.");
        }

        if (quantity <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(quantity));
        }

        if (unitPrice <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(unitPrice));
        }

        _lines.Add(new OrderLine(productId, quantity, unitPrice));
    }

    public void Submit()
    {
        if (Status != OrderStatus.Draft)
        {
            throw new InvalidOperationException("Only draft orders can be submitted.");
        }

        if (!_lines.Any())
        {
            throw new InvalidOperationException("Cannot submit an empty order.");
        }

        // Save Order

        Status = OrderStatus.Submitted;
    }
}

public class OrderLine
{
    public OrderLine(Guid productId, int quantity, decimal unitPrice)
    {
        ProductId = productId;
        Quantity = quantity;
        UnitPrice = unitPrice;
    }

    public Guid ProductId { get; }
    public int Quantity { get; }
    public decimal UnitPrice { get; }
    public decimal Total => Quantity * UnitPrice;
}

public enum OrderStatus
{
    Draft = 0,
    Submitted = 1,
    Cancelled = 2
}

Key observation: the domain layer expresses business concepts directly.

  • “Order must contain at least one line item” lives close to the Order aggregate
  • Status transitions are enforced inside the entity
  • Nothing here references controllers, HTTP, or EF Core

Data source layer: persistence without domain leakage

The data source layer manages persistence.

It should:

  • Map domain entities to database tables
  • Provide repository interfaces and implementations
  • Handle transactions and database concerns

It should not:

  • Return HTTP-only models
  • Depend on HttpContext
  • Make business decisions

Repository interface:

public interface IOrderRepository
{
    Task AddAsync(Order order, CancellationToken cancellationToken = default);
    Task<Order?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
}

EF Core implementation:

public class EfCoreOrderRepository : IOrderRepository
{
    private readonly AppDbContext _dbContext;

    public EfCoreOrderRepository(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task AddAsync(Order order, CancellationToken cancellationToken = default)
    {
        _dbContext.Orders.Add(order);
        await _dbContext.SaveChangesAsync(cancellationToken);
    }

    public async Task<Order?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await _dbContext.Orders
            .Include(o => o.Lines)
            .SingleOrDefaultAsync(o => o.Id == id, cancellationToken);
    }
}

DbContext:

public class AppDbContext : DbContext
{
    public DbSet<Order> Orders => Set<Order>();
    public DbSet<OrderLine> OrderLines => Set<OrderLine>();

    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>(builder =>
        {
            builder.HasKey(o => o.Id);
            builder.Property(o => o.CustomerId).IsRequired();
            builder.Property(o => o.Status).IsRequired();
            builder.Property(o => o.CreatedAt).IsRequired();

            builder.HasMany(typeof(OrderLine), "_lines")
                .WithOne()
                .HasForeignKey("OrderId")
                .IsRequired();
        });

        modelBuilder.Entity<OrderLine>(builder =>
        {
            builder.HasKey("Id");
            builder.Property<Guid>("OrderId");
            builder.Property(l => l.ProductId).IsRequired();
            builder.Property(l => l.Quantity).IsRequired();
            builder.Property(l => l.UnitPrice).IsRequired();
        });
    }
}

The repository understands EF Core and the database schema. The domain does not.

How does this structure make change cheaper

The layered structure looks simple until you try to change things. That is where it earns its keep.

1. Business rule changes

Suppose the business wants new rules:

  • Orders below a specific total should be rejected
  • Some customers have higher minimum totals

Where does that logic go?

  • Not in the controller
  • Not in the repository

It belongs in the domain layer, near the Order aggregate or in domain services.

You might extend OrderService:

public async Task<Guid> CreateOrderAsync(
    Guid customerId,
    IReadOnlyCollection<OrderLineDto> lines)
{
    if (!lines.Any())
    {
        throw new InvalidOperationException("Order must contain at least one line item.");
    }

    var order = Order.Create(
        customerId,
        lines.Select(l => new OrderLine(l.ProductId, l.Quantity)).ToList());

    var minimum = await GetCustomerMinimumAsync(customerId);

    if (order.TotalAmount < minimum)
    {
        throw new InvalidOperationException(
            $"Order total must be at least {minimum} for this customer.");
    }

    await _orders.AddAsync(order);
    return order.Id;
}

private Task<decimal> GetCustomerMinimumAsync(Guid customerId)
{
    // Look up from a configuration service or customer settings
    return Task.FromResult(100m);
}

Presentation and data source layers stay unchanged.

2. Switching transports: HTTP today, messaging tomorrow

Imagine you want to process orders from a message queue and via HTTP.

With a layered design, you write a message handler that calls the same IOrderService:

public class OrderCreatedMessageHandler
{
    private readonly IOrderService _orderService;

    public OrderCreatedMessageHandler(IOrderService orderService)
    {
        _orderService = orderService;
    }

    public async Task HandleAsync(OrderCreatedMessage message)
    {
        var dtoLines = message.Lines
            .Select(l => new OrderLineDto(l.ProductId, l.Quantity))
            .ToList();

        await _orderService.CreateOrderAsync(message.CustomerId, dtoLines);
    }
}

public record OrderCreatedMessage(Guid CustomerId, List<OrderLineDto> Lines);

No duplication of rules. No extra data access logic. You add another presentation layer entry point.

3. Swapping EF Core for another persistence mechanism

If you ever need to switch persistence, the blast radius is clear.

  • Domain layer remains the same
  • Presentation layer remains the same
  • Only IOrderRepository implementations and AppDbContext related types change

You can introduce another repository, for example DapperOrderRepository, and swap registrations in the DI container.

How teams quietly destroy their layers

Codebases rarely lose layering due to a single catastrophic decision. They lose it through a stream of small, “temporary” shortcuts.

Shortcut 1: “Just this one query in the controller”

A developer needs a special report. They already have access to AppDbContext in the controller, so they write:

public class ReportsController : ControllerBase
{
    private readonly AppDbContext _dbContext;

    public ReportsController(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    [HttpGet("reports/orders")]
    public async Task<IActionResult> GetOrdersReport()
    {
        var data = await _dbContext.Orders
            .Include(o => o.Lines)
            .Where(o => o.CreatedAt >= DateTime.UtcNow.AddDays(-7))
            .ToListAsync();

        // Transform into view model
        return Ok(data);
    }
}

It works. It ships. It also teaches the team that controllers are fair game for data access. After a few months, there is no clear separation at all.

Better approach: move data access to a query service or repository and call that from the controller.

Shortcut 2: Repositories returning view models

Another developer builds a dashboard. They want to avoid extra mapping, so they let the repository spit out DTOs used by the UI.

public interface IOrderRepository
{
    Task<IReadOnlyCollection<OrderSummaryDto>> GetRecentSummariesAsync();
}

That feels efficient. It also tangles the data source layer with a specific presentation need.

Six months later, a background worker wants a different representation, and the repository keeps growing special cases.

Better approach: keep repositories returning domain objects or well-defined query models that are not tied directly to controllers.

Shortcut 3: Domain services reading HttpContext

Sometimes, domain services need user information or tenant context. The easy path is to inject IHttpContextAccessor directly.

public class DiscountService
{
    private readonly IHttpContextAccessor _accessor;

    public DiscountService(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    public decimal GetDiscount()
    {
        var user = _accessor.HttpContext?.User;
        // ...
    }
}

This locks the domain to ASP.NET Core. Reusing the domain in background services or tests becomes painful.

Better approach: define an abstraction such as ICurrentUser or ICurrentTenant in the domain layer, and implement it in the presentation or infrastructure layer using HttpContext.

Enforcing layers with project structure

Code style and good intentions are not enough. The solution’s physical structure should reinforce the boundaries.

A common layout:

  • MyApp.Domain
    • Entities, value objects, domain services, domain interfaces
  • MyApp.Application (optional, if you separate application from pure domain)
    • Use cases, application services, DTOs
  • MyApp.Infrastructure
    • EF Core mappings, repositories, integration with external services
  • MyApp.Web
    • ASP.NET Core host, controllers, endpoints, filters

Dependency rules:

  • MyApp.Web references MyApp.Domain and MyApp.Application
  • MyApp.Infrastructure references MyApp.Domain and MyApp.Application
  • MyApp.Domain does not reference any other project
  • MyApp.Application references MyApp.Domain only

You can even add build checks or analyzers to prevent forbidden references.

Raising the stakes: what you lose without layers

If this sounds abstract, consider the cost of ignoring it.

  • You lose the ability to isolate a single change. Every change becomes a search through controllers, EF queries, and domain objects.
  • You lose optionality. Moving to messaging, splitting services, or swapping databases becomes nearly impossible without a rewrite.
  • You lose trust. Diagrams and architecture documents claim there are layers. The code says otherwise. Eventually, the team stops listening to both.

Layered architecture will not save you from every problem. It does something more modest and more powerful:

It gives you clear seams to cut, adapt, and evolve.

A simple challenge for your current system

Pick one feature in your application. Then:

  1. Identify the presentation, domain, and data source pieces.
  2. Count how many times each one crosses its boundary.
  3. Move a single business rule out of a controller into a domain service or aggregate.
  4. Move a single data access concern from a controller to a repository.

You do not fix an entire enterprise codebase in one refactor. You fix it one layer at a time, one leak at a time.

Once your controllers stop talking to SQL and your repositories stop knowing HTTP, everything else gets easier.

The post Stop Letting Your Controllers Talk to SQL: Layered Architecture in ASP.NET Core first appeared on Chris Woody Woodruff | Fractional Architect.

Read the whole story
alvinashcraft
45 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Enterprise Patterns, Real Code: Implementing Fowler’s Ideas in C#

1 Share

Most enterprise systems already use patterns from Martin Fowler’s Patterns of Enterprise Application Architecture. The twist is that many teams use them without naming them, half implement them and then wonder why the codebase fights back.

If your ASP.NET solution contains controllers that talk straight to SQL, services that return HTTP responses, and entities that call SaveChanges, you are already remixing Fowler’s patterns. You are just doing it implicitly and at great expense.

This series takes the opposite route. We will name the patterns, show where they fit, and implement each one in C# with concrete examples.

You will see where these patterns help, where they hurt, and how they combine into real architectures instead of diagram fantasies.

How this series will work

In each article, I will:

  • Explain a pattern in plain language, with the original intent from Fowler’s catalog
  • Show how it typically shows up in C# and .NET projects
  • Give a focused example of when to use it and when to avoid it
  • Connect it to neighboring patterns so you see design options, not isolated tricks

This introduction maps the territory. Every item in the index below links to a short description, a definition, and an example scenario. Each of those sections will become a full post with code.

Pattern index

Use these links to jump to the pattern you care about right now.

  • Layered Architecture
  • Transaction Script
  • Domain Model
  • Service Layer
  • Active Record
  • Data Mapper
  • Repository
  • Unit of Work
  • Identity Map
  • Lazy Load
  • Front Controller
  • Model View Controller (MVC)
  • Data Transfer Object (DTO)

Each section below is both a preview and a contract for the dedicated C# article that will follow.

Layered Architecture Pattern

Definition

Layered Architecture splits an application into distinct layers with clear responsibilities. Fowler’s baseline is:

  • Presentation: handles input and output
  • Domain: holds business rules and domain logic
  • Data source: manages persistence and integration with data stores

The core rule is brutal and simple: if your controllers know SQL or your repositories know HTTP, the layers have already collapsed.

Where to use it

Layered Architecture fits:

  • ASP.NET Core applications that will grow beyond a few controllers
  • Systems where different teams own UI, business rules, and infrastructure
  • Codebases that must survive several technology shifts over their lifetime

In the dedicated post you will see a C# solution where projects align with layers, controllers stay thin, domain services stay ignorant of transport, and repositories encapsulate EF Core without leaking it upward.

Transaction Script Pattern

Definition

Transaction Script organizes business logic as procedures that handle a single request or use case end to end. Each script:

  • Reads input
  • Performs calculations and decisions
  • Persists changes

There is minimal domain modeling. The focus stays on the flow of a transaction.

Where to use it

Transaction Script works best when:

  • The domain logic is simple and shallow
  • You are building reports, admin utilities, or migration tools
  • You need results quickly and long term complexity is limited

In C#, this often appears as an application service or handler class that works directly with DbContext and simple DTOs. In the article you will see both the benefits and the trap: it feels efficient until rules start to repeat across scripts.

Domain Model Pattern

Definition

Domain Model concentrates business rules inside a rich object model. Entities and value objects express invariants and behavior.

Instead of treating data as passive structures, you treat the domain as the center of gravity. Controllers and repositories orbit around it rather than injecting rules into every edge of the system.

Where to use it

Domain Model earns its weight when:

  • The business rules are complex and interdependent
  • Invariants matter more than raw throughput
  • You expect requirements to evolve frequently

In C# this means entities with methods that enforce rules, factories that control creation, and services that orchestrate multiple aggregates. The dedicated post will show an aggregate in code, along with tests that lock in behavior before you worry about EF mapping.

Service Layer Pattern

Definition

Service Layer defines a set of application operations that sit between the outside world and the domain model. It:

  • Coordinates multiple domain objects
  • Handles transactions and security policies
  • Exposes a clear API for controllers, message handlers, or other clients

It is the point where use cases live.

Where to use it

Service Layer fits when:

  • You have multiple clients hitting the same core logic: web, background jobs, workers
  • You want to expose a stable application API while the UI evolves
  • Cross cutting concerns such as logging, permissions, and transaction boundaries must stay consistent

In .NET this often becomes a set of application service classes injected into controllers and workers. In the article you will see a C# service layer that makes HTTP a detail, not the boss.

Active Record Pattern

Definition

Active Record merges domain objects with persistence. Each entity:

  • Maps directly to a database row
  • Contains business logic
  • Knows how to load and save itself

Fowler treats it as a close fit for simple domains where the object model mirrors the database closely.

Where to use it

Active Record suits:

  • Small systems with straightforward tables
  • Prototypes where getting something working matters more than deep abstraction
  • Places where simple CRUD with light behavior is enough

In C#, you often see Active Record flavor when EF Core entities call SaveChanges directly or static methods perform global queries. The dedicated post will show a disciplined version of Active Record and explain when to retire it in favor of a separate Data Mapper.

Data Mapper Pattern

Definition

Data Mapper sits between domain objects and the database. It:

  • Loads domain objects from data stores
  • Persists changes back
  • Shields the domain from knowledge of how persistence works

The domain classes stay persistence ignorant. The mapper takes on the burden of translation.

Where to use it

Data Mapper pays off when:

  • You have a rich Domain Model
  • The database schema must evolve independently of the object model
  • You want to test domain logic without a database in the way

In .NET, EF Core already plays the Data Mapper role. The article will show how to design domain classes that do not depend on EF, then map them using configurations and repositories that wrap the mapper.

Repository Pattern

Definition

Repository represents a collection-like interface for accessing aggregates. It:

  • Hides queries and persistence details
  • Exposes methods that work in domain terms, such as GetById, FindActiveForCustomer, Add, Remove
  • Lets the domain talk in its own language instead of in SQL or query APIs

Fowler includes Repository in the object relational patterns as a way to further isolate domain logic from data access.

Where to use it

Repository helps when:

  • The same aggregate appears across many use cases
  • You want consistent access patterns for aggregates
  • You expect to support multiple query strategies or stores behind the same domain interface

In C#, this usually means interface definitions in the domain layer and implementations in an infrastructure project. The dedicated post will include concrete repository designs, and also examples of where a repository introduces more indirection than it earns.

Unit of Work Pattern

Definition

Unit of Work tracks changes to domain objects during a business transaction and writes them out as a single logical batch. It:

  • Records inserts, updates, and deletes
  • Coordinates commit or rollback
  • Provides a boundary for transactional behavior

Fowler presents it as a way to stop writes from spreading unpredictably through a codebase.

Where to use it

Unit of Work is valuable when:

  • A single operation touches multiple aggregates or tables
  • You need clear transactional boundaries for consistency
  • You want to keep domain logic free of save calls

In .NET, DbContext already behaves as a Unit of Work, yet many codebases hide that fact. The article will show how to embrace this pattern explicitly and how to wrap EF Core in a higher level unit of work abstraction when needed.

Identity Map Pattern

Definition

Identity Map ensures that each logical entity from the database exists only once in memory per scope. It:

  • Tracks loaded objects by identity
  • Returns existing instances instead of creating new ones for the same key
  • Helps avoid inconsistent in memory states for the same row

This pattern often works with Unit of Work and Data Mapper.

Where to use it

Identity Map matters when:

  • The same entity is loaded through different paths in one request
  • You attach domain behavior to entities and depend on reference equality
  • You care about performance costs of repeated materialization

ORMs such as EF Core implement identity maps under the surface. The dedicated post will explain what EF is doing for you and show how to apply Identity Map explicitly when you move outside of ORMs or use multiple contexts.

Lazy Load Pattern

Definition

Lazy Load defers loading of related data until it is actually needed. Instead of fetching entire object graphs, you:

  • Load a root entity
  • Represent associations as placeholders
  • Trigger actual loading when code accesses the association

The pattern targets performance and memory by avoiding unnecessary work.

Where to use it

Lazy Load helps when:

  • Most use cases do not need full graphs
  • Some navigations are expensive or remote
  • You have to control query explosions carefully

In .NET, EF Core can use lazy loading proxies, or you can code your own lazy associations. The article will show both approaches and highlight the risk: invisible queries that surprise you in performance profiles.

Front Controller Pattern

Definition

Front Controller centralizes request handling for a web application. Instead of letting every page or endpoint own its own entry point, you:

  • Route all requests through a single handler
  • Apply cross cutting logic in one place
  • Delegate to controllers or handlers for detailed work

Fowler introduces it as a response to duplicated request handling logic.

Where to use it

Front Controller aligns with:

  • Web applications that need consistent logging, authentication, and error handling
  • Systems that must make routing decisions based on shared policies
  • Architectures that use pipelines and middleware

In ASP.NET Core, the combination of the hosting pipeline and routing already forms a Front Controller. The dedicated post will show how to control that pipeline intentionally instead of treating it as framework magic.

Model View Controller (MVC) Pattern

Definition

Model View Controller splits UI logic into three parts:

  • Model: the underlying data and behavior
  • View: rendering logic
  • Controller: input handling and coordination

Fowler’s version focuses on server side web MVC.

Where to use it

MVC suits:

  • Applications with complex UI interactions that depend on domain rules
  • Teams that want clear separation between presentation logic and domain logic
  • Systems that must support multiple views on the same model

In ASP.NET Core MVC, controllers speak to application services, views render models, and domain rules stay out of both. The article will show how to keep controller code lean instead of letting it morph into a second application layer.

Data Transfer Object (DTO) Pattern

Definition

Data Transfer Object carries data across process boundaries. It:

  • Aggregates fields into a serializable shape
  • Avoids sending full domain objects across the wire
  • Provides a contract between services, clients, or layers

DTOs trade object richness for stability and clarity at integration points.

Where to use it

DTOs are worth the effort when:

  • You are exposing public APIs
  • Multiple clients consume your service, each with their own evolution pace
  • You want to keep domain classes internal to your application

In C#, DTOs typically appear as record types in API projects or as message contracts in messaging systems. The article will show mapping patterns between domain objects and DTOs and how to keep them from overflowing with accidental complexity.

What comes next

The rest of this series will go pattern by pattern:

  • Each pattern gets its own post
  • Each post includes C# examples, tests where relevant, and context from real projects
  • The focus stays on tradeoffs, not worship of diagrams

You can read the series start to finish, or you can drop directly into the pattern that matches the pain in your current system and work outward from there.

If your code already resembles these patterns, this series gives you language and structure. If it does not, the upcoming posts will show how to reshape it piece by piece without pausing delivery.

The post Enterprise Patterns, Real Code: Implementing Fowler’s Ideas in C# first appeared on Chris Woody Woodruff | Fractional Architect.

Read the whole story
alvinashcraft
51 seconds ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories