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
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
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!
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.