ISSUE 42
Software Architecture

Avoiding Code Duplication in Vertical Slice Architecture with .NET

An analysis of strategies to maintain DRY principles in Vertical Slice Architecture without sacrificing the decoupling benefits of feature-based organization.

Abhik Kumar Panda
Abhik Kumar Panda
Creator & Engineer
August 23, 2026 · 4 min read
Abstract representation of modular software slices and shared core components in a .NET architecture

Vertical Slice [[[Architecture](/blog/181874-meetings-one-missing-security-rule-the-tldv-leak-is-an-architecture-lesson)](/blog/single-process-architecture-in-rust-why-we-rejected-microservices-for-an-edge-ai-platform)](/blog/clean-architecture-in-flutter-with-bloc-a-practical-guide) (VSA) shifts the focus from layering (Presentation, Domain, Infrastructure) to feature-based organization. By encapsulating all code required for a specific business requirement—from the API endpoint to the database query—within a single slice, developers gain high cohesion and low coupling. However, this architectural style often triggers concerns regarding the ‘Don’t Repeat Yourself’ (DRY) principle. When multiple features require the same validation logic or data transformation, the temptation to revert to shared layers often threatens the integrity of the slices.

The challenge lies in distinguishing between accidental duplication and shared business invariants. If you find yourself duplicating code across slices, it is rarely a failure of the architecture itself, but rather a sign that your domain boundaries or abstractions require recalibration. Managing this requires a nuanced approach that prioritizes feature autonomy while leveraging common infrastructure where appropriate.

Understanding the Scope of Duplication

In a well-designed vertical slice, duplication is not inherently bad. If two slices share a piece of code, but that code changes for different reasons (different volatility), keeping them separate is actually a feature, not a bug. This is the core tenet of the Single Responsibility Principle applied to architecture: code that changes together should live together.

However, when duplication involves complex business logic or cross-cutting concerns, it creates maintenance overhead. The goal is to extract shared logic into ‘Shared Kernels’ or ‘Common Modules’ only when the logic is truly stable and represents a shared invariant across the entire domain.

Strategic Extraction: The Shared Kernel Pattern

For logic that must be consistent across the entire application—such as specific domain entities, value objects, or core business rules—a Shared Kernel is the appropriate mechanism. In .NET, this is best implemented as a separate project or a shared namespace that is strictly limited in scope.

namespace Core.Shared.Domain;

public record Money(decimal Amount, string Currency) {
    public static Money operator +(Money a, Money b) {
        if (a.Currency != b.Currency) throw new ArgumentException("Currency mismatch");
        return new Money(a.Amount + b.Amount, a.Currency);
    }
}

By placing stable, immutable value objects in a shared project, you ensure consistency across slices without forcing features to depend on each other. The key is to keep this layer thin; if it becomes a dumping ground for all shared logic, you have essentially rebuilt a monolithic layer.

Leveraging Mediator Patterns and Behaviors

Cross-cutting concerns like logging, transaction management, or authentication should never be duplicated in each slice. In the .NET ecosystem, the MediatR library provides a powerful way to handle these concerns via pipeline behaviors.

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
    {
        var context = new ValidationContext<TRequest>(request);
        var failures = _validators.Select(v => v.Validate(context)).SelectMany(r => r.Errors).ToList();
        if (failures.Any()) throw new ValidationException(failures);
        return await next();
    }
}

This approach removes the need to manually invoke validation or logging in every single request handler. By injecting these behaviors into the MediatR pipeline, you centralize the execution logic while keeping the actual handler logic inside the slice.

When to Favor Duplication over Abstraction

One of the most difficult lessons in architectural design is knowing when to accept duplication. If two features perform similar database queries but have different performance requirements or evolve at different speeds, creating a shared repository or service will couple them together.

Duplication is far cheaper than the wrong abstraction. If you couple two features prematurely to avoid a few lines of code, you create a rigid system that is difficult to refactor later.

If a piece of code is duplicated across three or four slices, and you notice that modifying one slice now requires you to update the others to avoid breaking changes, only then should you consider refactoring that code into a shared service. If the code is static and rarely changes, duplication is often the most maintainable path.

Conclusion

Vertical Slice Architecture in .NET is not about eliminating all repetition; it is about managing dependencies. By using Shared Kernels for immutable domain concepts and MediatR pipeline behaviors for cross-cutting concerns, you can maintain a clean, organized codebase without falling into the trap of over-engineering.

Always evaluate whether a shared abstraction provides actual value or if it simply adds unnecessary coupling. When in doubt, prefer duplication until the pattern becomes clear and the cost of maintenance outweighs the cost of decoupling. This pragmatic approach ensures your slices remain independent, testable, and aligned with the actual business requirements of your application.

Share Twitter LinkedIn
Abhik Kumar Panda
CONTRIBUTING FELLOW

Abhik Kumar Panda

Creator & Engineer

Software engineer and creator passionate about technical writing, systems architecture, and AI.

Continue Reading

Stay in the loop.

Get new engineering articles, systems ideas, and things worth knowing — straight to your inbox.

No spam. Just useful things.
Unsubscribe anytime.