web-dev-qa-db-fra.com

Quel est le but de la logique de domaine / entreprise dans les classes lors de la présence de référentiels?

De mon expérience, je pense que posséder des cours/modèles sans comportement que dans ma demande, à côté de leurs référentiels n'est pas bon oop. Mais, c'était la façon dont j'ai mis en œuvre le motif de référentiel. Je fais juste partout où j'ai besoin d'une instance de référentiel, pour effectuer certaines actions. Le résultat de cette approche était que toutes mes classes de domaine n'avaient pas de comportement.

Ils n'étaient que des objets détenant des données sans méthodes. Mon professeur m'a dit que j'utilisais des modèles minces et que je devrais m'efforcer de faire des modèles de graisse. En réponse à ces commentaires, j'ai mis en œuvre une logique commerciale dans les classes, mais j'ai rencontré certains problèmes:

Exemple :

public class Movie
{
    private MovieRepository movieRepo = new MovieRepository(new MovieDbContext());
    private PostRepository postRepo = new PostRepository(new PostDbContext());

    public decimal Rating { get; set; }
    public List<Post> Posts { get; set; }

    public void Rate(User user, Movie movie)
    {
        if(movie.Rating < 0 || movie.Rating > 10) 
        {
            throw new Exception("The rating must be a digit between 0 and 10");
        }
        this.Rating = movie.Rating;
        movieRepo.RateMovie(user.Id, movie.Id, (int)movie.Rating);
    }

    public void AddPost(User user, Movie movie, string text)
    {
        int maxId = 0;
        foreach (Post p in Posts)
        {
            if (p.Id > maxId)
            {
                maxId = p.Id;
            }
        }

        Post post = new Post(maxId + 1, user, text, DateTime.Now);
        this.Posts.Add(post);

        postRepo.AddPost(user.Id, movie.Id, text, DateTime.Now);
    }


}

Comme vous pouvez le constater dans l'exemple ci-dessus, je gère d'abord une logique de domaine pour effectuer des actions sur la classe elle-même, puis je le persiste à la base de données à l'aide d'un référentiel. Mais pourquoi ai-je même ajouté les messages à la classe elle-même dans la méthode addPost, lorsque je le gère avec un référentiel juste après?

Est-ce juste parce que vous pouvez maintenant voir les modifications apportées directement à l'écran, en mémoire? Ou est-ce que l'objectif de la logique commerciale est uniquement de valider l'entrée du paramètre, comme indiqué dans la méthode du tarif de la vidéo? Mais ces types d'exceptions peuvent également être lancées dans le référentiel si la méthode du référentiel vérifie également si un chiffre est compris entre 0 et 10. Mais je pense que un référentiel ne devrait pas être préoccupé par cela. Le référentiel doit seulement traduire les informations sur les informations de base de données, ai-je raison?

Mais cela dit que, je ne comprends pas exactement la nécessité d'effectuer des changements sur l'objet lui-même, lorsque vous le gérez avec un référentiel. Au lieu de cela, vous pouvez à cela (n'importe où dans votre application, pour afficher l'objet utilisateur):

postRepo.AddPost(2, 1, "Nice movie!", DateTime.Now);
User user = userRepo.GetById(2);

Quels sont les avantages et les inconvénients de cette différence?

6
Maikkeyy

Quel est le but de Logique Domaine/Business dans les classes lorsque j'ai référentiels?

C'est un peu comme demander:

Quel est le but de voitures quand nous avons garages?

La raison en est que les classes d'entreprise et les référentiels résolvent différents problèmes et sont donc des préoccupations différentes dans la demande. En tant que tel, ils doivent être dans des classes séparées.

L'objectif principal d'un référentiel est de fournir une couche d'abstraction entre la persistance et votre code. Commutation de fournisseurs de base de données, ou même des supports de stockage (base de données, fichier plat, service Web, etc.) n'auront pas d'importance en dehors de vos classes de référentiel.

Le but d'une classe d'entreprise est d'appliquer la logique commerciale.

Le but de séparer la logique des entreprises de la logique de persistance est donc afin que vous puissiez appliquer la logique commerciale sans vous soucier de la persistance. Peut-être que vous avez une importation de données. Tests unitaires N'ayez pas besoin d'une base de données pour valider les règles commerciales.

Pensez aux exigences que vous avez maintenant:

  1. A Note du film Doit avoir un utilisateur
  2. A Note du film Doit avoir un film
  3. A Note du film Doit être compris entre 0 et 10
  4. Si A Utilisateur a précédemment évalué a film, la note sera modifiée
  5. Si A User n'a pas été évalué a film, la note sera ajoutée
  6. A utilisateur Doit avoir un nom d'utilisateur
  7. A Utilisateur a zéro ou plusieurs évaluations de films
  8. A utilisateur peut évaluer les films

Aucun de ceux-ci n'a quoi que ce soit à voir avec l'insertion, la mise à jour, la sélection ou la suppression de données dans la base de données. En fait, ces mêmes règles pourraient être appliquées si vous passez la persistance à un fichier XML.

Maintenant, considérons ces classes d'entreprise:

Tout d'abord, un stub stupide pour le film:

public class Movie
{
    public int Id { get; set; }
    public string Title { get; set; }
}

Nous savons maintenant qu'une note de film est composée de trois choses: un utilisateur; un film et une note de numéro.

La classe d'utilisateurs:

public class User
{
    public User(string username)
    {
        // Requirement #6
        if (string.IsNullOrEmpty(username))
            throw new ArgumentNullException("username");

        Username = username;

        // Requirement #7
        movieRatings = new Collection<MovieRating>();
    }

    // Requirement #6
    public string Username { get; private set; }

    // Requirement #7
    private ICollection<MovieRating> movieRatings;

    // Requirement #6
    public IEnumerable<MovieRating> MovieRatings
    {
        get { return movieRatings; }
    }

    // Requirement #4
    public MovieRating GetRating(Movie movie)
    {
        return MovieRatings.FirstOrDefault(rating => rating.Movie.Id == movie.Id);
    }

    // Requirement #8 and #1
    public MovieRating RateMovie(Movie movie, int rating)
    {
        // Requirement #2
        if (movie == null)
            throw new ArgumentNullException("movie");

        var movieRating = GetRating(movie);

        if (movieRating == null)
        {
            // Requirement #5
            movieRating = new MovieRating(this, movie, rating);
            movieRatings.Add(movieRating);
        }
        else
        {
            // Requirement #4
            movieRating.ChangeRating(rating);
        }

        return movieRating;
    }
}

La classe Movicerating:

public class MovieRating
{
    // Requirement #8 and #1
    internal MovieRating(User user, Movie movie, int rating)
    {
        // Requirement #1
        if (user == null)
            throw new ArgumentNullException("user");

        // Requirement #2
        if (movie == null)
            throw new ArgumentNullException("movie");

        // Requirement #3
        if (IsValidRating(rating))
            throw new ArgumentOutOfRangeException("rating", "Rating must be between " + MIN_RATING + " and " + MAX_RATING);

        User = user;
        Movie = moview;
        Rating = rating;
    }

    public User User { get; private set; }
    public User Movie { get; private set; }
    public int Rating { get; private set; }

    // Requirement #3
    public const int MIN_RATING = 0;
    public const int MAX_RATING = 10;

    // Requirement #3
    public static bool IsValidRating(int rating)
    {
        return rating >= MIN_RATING && rating <= MAX_RATING;
    }

    // Requirement #4
    public void ChangeRating(int newRating)
    {
        // Requirement #3
        if (IsValidRating(newRating))
            throw new ArgumentOutOfRangeException("newRating", "Rating must be between " + MIN_RATING + " and " + MAX_RATING);

        Rating = newRating;
    }
}

J'ai mis des commentaires dans le code C # pour illustrer la manière dont les classes de l'entreprise (utilisateur, film et mobissement) appliquent la logique commerciale.

Caractéristiques remarquables de ce code:

  • Le constructeur de la classe MovieRating est marqué internal restreindre qui peut créer des instances de cette classe à coder à l'intérieur du même assemblage que la classe.

  • La méthode RateMovie sur la classe User est public et est la seule chose qui crée les objets Movicerating. Cela garantit que vous avez correctement lié le bon utilisateur avec le film lors de l'ajout à la collection de notes de film privées

  • Les User.movieRatings Le champ est privé afin que la classe d'utilisateur ait pleinement le contrôle de la création de Movieurs

  • Les User.MovieRatings la propriété est une IEnumerable<MovieRating> Donc, le code client doit appeler la méthode RateMovie sur la classe User afin d'évaluer un film pour cet utilisateur.

  • Les cotes minimales et maximales sont codifiées en tant que constantes sur la classe Movicerating

  • Une méthode statique IsValidRating est publique de sorte que tout code, que ce soit ou non un objet de déploiement soit disponible, a un endroit central pour savoir si une note est valide ou non. Pensez à former des validateurs de terrain dans la présentation/la couche Web de votre application.

  • La méthode RateMovie trouve une note existante et la modifie, ou crée un nouvel objet Movicerating si on n'existe pas (exigence n ° 4)

  • Aucune de ces fonctionnalités n'a n'importe quoi à voir comment les données sont insérées ou mises à jour

12
Greg Burghardt