In this lab, you'll get familiar with the sample application, run tests, and analyze the "Spaghetti Code" pattern implementation in the 01_Spaghetti folder. This will help you understand the problems that cross-cutting concerns can create when mixed with business logic, setting the stage for learning better patterns in subsequent labs.
By the end of this lab, you will be able to:
- Clone and set up the project
- Build and run tests successfully
- Identify cross-cutting concerns in code
- Understand the problems with mixing concerns in a single service
- Analyze code complexity and maintainability issues
- Create endpoints following the pattern-based routing convention
- .NET 10.0 SDK or later (Download here)
- Git
- A code editor (Visual Studio, VS Code, Rider, etc.)
- Basic understanding of C# and ASP.NET Core
git clone https://github.com/NimblePros/RefactorToPipelineArchitecture
cd RefactorToPipelineArchitectureCheck that you have the correct .NET SDK installed:
dotnet --versionYou should see version 10.0 or later.
Running the tests will restore and build.
dotnet testThis runs all integration tests in the solution. You should see output indicating which tests passed or failed.
The tests use [Theory] and [InlineData] to test all patterns with the same test logic:
[Theory]
[InlineData("Spaghetti")] // Lab 1 (current)
[InlineData("Template")] // Lab 2
[InlineData("Decorator")] // Lab 3
[InlineData("Pipeline")] // Lab 4
[InlineData("PipelineAttributes")] // Lab 4
public async Task GetRoles_AsGuest_ReturnsForbidden(string pattern)
{
var response = await Client.GetAsync($"/{pattern}/Roles");
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
}For Lab 1, focus on the tests that use the "Spaghetti" pattern. The other tests will mostly fail until those labs have been completed.
The project demonstrates multiple patterns for managing cross-cutting concerns:
src/NimblePros.DAB.Web/
├── 01_Spaghetti/ ← Focus on this for Lab 1
│ ├── Services/
│ │ └── RoleManagerService.cs
│ ├── Endpoints/
│ │ ├── List.cs (GET /Spaghetti/Roles)
│ │ └── AddRole.cs (POST /Spaghetti/Roles)
│ └── Abstractions/
│ └── IRoleManagerService.cs
├── 02_Template/ ← For Lab 2
├── 03_Decorator/ ← For Lab 3
├── 04_Chain/ ← For Lab 4
├── Data/ ← Database context
└── Model/ ← Domain models
All endpoints follow the pattern /PatternName/Resource:
- Spaghetti:
/Spaghetti/Roles - Template:
/Template/Roles - Decorator:
/Decorator/Roles - Pipeline:
/Pipeline/Roles - PipelineAttributes:
/PipelineAttributes/Roles
This makes it easy to compare the behavior of different patterns for the same operation.
01_Spaghetti/Services/RoleManagerService.cs- The main service with all concerns mixed in01_Spaghetti/Endpoints/List.cs- GET endpoint at/Spaghetti/Roles01_Spaghetti/Endpoints/AddRole.cs- POST endpoint at/Spaghetti/Roles01_Spaghetti/Abstractions/IRoleManagerService.cs- Service interfacetests/NimblePros.DAB.IntegrationTests/Endpoints/ListRolesEndpointTests.cs- Parameterized integration tests
You can do this later if you run out of time during the workshop.
The term "spaghetti code" refers to code where different concerns are tangled together, making it hard to understand, maintain, and test. In this example, the RoleManagerService class mixes:
- Business Logic (data access)
- Authorization (role checks)
- Logging (diagnostic messages)
- Caching (performance optimization)
- Error Handling (try-catch blocks)
- Validation (input checking)
Open src/NimblePros.DAB.Web/01_Spaghetti/Services/RoleManagerService.cs and examine the ListAsync() method (lines 24-55).
Questions to consider:
-
How many different responsibilities does this method have?
- Count the distinct concerns (hint: look for authorization checks, logging calls, caching logic, error handling, and data access)
-
What is the core business logic?
- Try to identify the single line that represents the actual business purpose of this method
-
What percentage of the code is cross-cutting concerns vs. core logic?
- Count lines dedicated to logging, caching, authorization, and error handling vs. the actual data retrieval
Now examine the AddAsync() method (lines 57-117).
Questions to consider:
-
How many concerns are mixed in this method?
- Identify authorization, logging, validation, error handling, caching, and business logic
-
What makes this method difficult to test?
- Think about what you'd need to mock or setup to test just the validation logic, or just the authorization
Problems with the Spaghetti Pattern:
- Violation of Single Responsibility Principle: The service does too many things
- High Coupling: Changes to one concern (e.g., caching) require editing the service
- Low Cohesion: Unrelated concerns (logging, authorization, caching) are mixed together
- Difficult to Test: Must mock multiple dependencies to test one aspect
- Code Duplication: Same patterns repeated in every method
- Hard to Maintain: Business logic is obscured by infrastructure code
- Inflexible: Can't easily add/remove concerns or change their order
In Lab 2, you'll learn about the Template Method Pattern - a common approach for separating concerns. You'll refactor the code to extract cross-cutting concerns into a base class, making the business logic clearer while still keeping concerns together in an inheritance hierarchy.
You'll create endpoints at /Template/Roles that behave identically to /Spaghetti/Roles but use a cleaner implementation.
Before moving to Lab 2, make sure you can answer:
- What are cross-cutting concerns?
- What are at least 5 problems with the spaghetti code pattern?
- How many lines of code in
ListAsync()are core business logic vs. cross-cutting concerns? - Why is the spaghetti pattern difficult to test?
- What would happen if you needed to add a new cross-cutting concern (e.g., performance monitoring)?
- How does the routing convention help organize and compare different patterns?
- Why use parameterized tests with
[Theory]instead of separate test classes?
- Ardalis.Result Pattern: Used for clean result handling - GitHub
- FastEndpoints: Lightweight alternative to MVC controllers - Docs
- Single Responsibility Principle: Martin Fowler
- xUnit Theory and InlineData: xUnit Documentation
When you're ready, proceed to Lab 2 where you'll explore the Template Method Pattern as a first step toward better code organization.
Questions or Issues? Open an issue in the GitHub repository or ask your instructor for clarification.