Part 5: Deepening the Service Layer’s Role in Our Architecture

Kaan Celiker
3 min readOct 30, 2024

Introduction

The Service Layer in a layered architecture is critical for separating business logic from both the data and API layers. It serves as a middle layer that manages business rules, directs data flow, and integrates calls to stored procedures (SPs) and standard CRUD operations. This part details how we establish a robust Service Layer, handling complex processes and maintaining a clean separation of responsibilities within our application.

Why the Service Layer Matters

  1. Centralized Business Logic: By placing business rules and validation in the Service Layer, we ensure that both the data layer and the API remain clean and focused on their specific tasks.
  2. Reusability and Scalability: The Service Layer allows us to add or modify logic without disrupting other layers, making it easier to scale and maintain the application.
  3. Efficient Error Handling: By handling errors in the Service Layer, we prevent exceptions from propagating up to the API layer, improving stability and user experience.
  4. Stored Procedure Integration: By incorporating SPs directly in this layer, we create an optimized system for complex data retrieval and manipulation.

Step-by-Step Setup of the Service Layer

We’ll now go through setting up the Service Layer, defining core methods for CRUD operations and integrating SP calls. Each service will correspond to a repository in the DAL and will expose operations required by the API.

1. Example: MovieService — CRUD and Stored Procedure Support

The MovieService class will support basic CRUD operations as well as SP-based operations, such as retrieving top-rated movies. For consistency, we use the WithSp naming convention to separate SP-based methods from CRUD.

MovieService.cs

using MovieSeries.CoreLayer.Entities;
using MovieSeries.RepositoryLayer.Interfaces;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MovieSeries.ServiceLayer.Services
{
public class MovieService : IMovieService
{
private readonly IMovieRepository _movieRepository;
public MovieService(IMovieRepository movieRepository)
{
_movieRepository = movieRepository;
}
// CRUD Methods
public async Task<IEnumerable<Movie>> GetAllMoviesAsync()
{
return await _movieRepository.GetAllMoviesAsync();
}
public async Task AddMovieAsync(Movie movie)
{
await _movieRepository.AddMovieAsync(movie);
}
// Stored Procedure Method
public async Task<IEnumerable<Movie>> GetTopRatedMoviesWithSpAsync(int topCount)
{
return await _movieRepository.GetTopRatedMoviesWithSpAsync(topCount);
}
}
}

In MovieService, CRUD methods like GetAllMoviesAsync and AddMovieAsync delegate basic data tasks to the DAL. However, GetTopRatedMoviesWithSpAsync is an SP-based method that uses stored procedures to retrieve optimized data.

2. Implementing Validation and Business Logic in the Service Layer

The Service Layer also acts as a filter, enforcing data validation and applying business rules. For instance, we could ensure that movies cannot have duplicate titles or that a review score remains within acceptable limits.

Example: Adding Validation in MovieService

public async Task AddMovieAsync(Movie movie)
{
var existingMovies = await _movieRepository.GetAllMoviesAsync();
if (existingMovies.Any(m => m.Title == movie.Title))
{
throw new ArgumentException("A movie with the same title already exists.");
}
await _movieRepository.AddMovieAsync(movie);
}

By integrating validation directly in the AddMovieAsync method, we can enforce rules without relying on the Controller, keeping the API clean and focused on request handling.

3. Error Handling in the Service Layer

The Service Layer is also a great place to handle exceptions. Instead of passing errors directly to the API, we handle them in the Service Layer, potentially logging the error or transforming it into a user-friendly message.

Example: Error Handling in MovieService

public async Task<IEnumerable<Movie>> GetTopRatedMoviesWithSpAsync(int topCount)
{
try
{
return await _movieRepository.GetTopRatedMoviesWithSpAsync(topCount);
}
catch (Exception ex)
{
// Log error, provide a generic message, or rethrow
throw new ApplicationException("An error occurred while retrieving top-rated movies.", ex);
}
}

Summary

In Part 5, we enhanced our Service Layer, ensuring it handles all interactions between the API and the DAL. With its role in managing business logic, enforcing validation, handling errors, and supporting SPs, the Service Layer is a critical piece of our clean, scalable architecture.

Next Part

--

--

Kaan Celiker
Kaan Celiker

Written by Kaan Celiker

Assistant Software Test Specialist

No responses yet