Search code examples
c#classstructuresoc

Separation of Concerns/Code Structuring in a class based environment (C# used as example)


I have always wondered what the best practice is for separating code in a class based language. As an example, I made a project that handles api interaction with my web api. I want to know what the right option is to go with, or another suggestion.

Example 1

Project Files

  • Api.cs
  • DataTypes
    • Anime.cs
    • Episode.cs

Api.cs

public class Api
{
    public static async Task<List<Anime>> GetAnimesByKeyword(string keyword)
    {
        // Execute api request to server
        return result;
    }

    public static async Task<List<Episode>> GetEpisodesByAnime(Anime anime)
    {
        // Execute api request to server
        return result;
    }

}

DataTypes -> Anime.cs

public class Anime
{
    public string Name { get; set; }
    public string Summary { get; set; }
    // Other properties
}

DataTypes -> Episode.cs

public class Episode
{
    public string Name { get; set; }
    public Date ReleaseDate { get; set; }
    // Other properties
}

Or example 2

Project Files

  • Api.cs
  • DataTypes
    • Anime.cs
    • Episode.cs

Api.cs

public class Api
{
    // Nothing for now
}

DataTypes -> Anime.cs

public class Anime
{
    public static async Task<Anime> GetById(int id)
    {
        return result;
    }

    public string Name { get; set; }
    public string Summary { get; set; }
    // Other properties
}

DataTypes -> Episode.cs

public class Episode
{
    public static async Task<List<Episode>> GetEpisodesByAnime(Anime anime)
    {
        return result;
    }

    public string Name { get; set; }
    public Date ReleaseDate { get; set; }
    // Other properties
}

What of these 2 is the preferred way of structuring the code, or is there a better way to do this. It might seem insignificant, but it does matter to me.

Thanks for helping me out!


Solution

  • In general, follow the Single Responsibility Principle. In practice this means you have simple objects that are data-only and more complex service classes that do work like loading from an external service or database.

    Your second example mixes concerns AND it binds these two classes together tightly (Episode now depends on Anime). You can also see how it's hard to decide which class to put that loading method on: should it be anime.GetEpisodes() or Episode.GetEpisodesByAnime()? As the object graph gets more complex this escalates.

    Later you may well want a different data transfer object for an entity. Having simple data-only objects makes it easy to add these and to use Automapper to convert.

    But (on your first example) don't use static methods because that makes your service class harder to test. One service may depend on another (use dependency injection) and to test each in isolation you don't want to have static methods.