Chiamare un metodo di servizio da un altro metodo di servizio

1

Nel modello di repository, è una "cattiva idea" chiamare un metodo di servizio da un altro metodo di servizio?

Nella maggior parte dei casi, finisco naturalmente per chiamare i metodi di repository dai miei metodi di servizio. Tuttavia, in alcuni casi, chiamare un metodo di servizio da un altro metodo di servizio sembra avere senso perché il metodo di servizio esistente gestisce in modo appropriato l'attività in corso.

Diciamo che ho i seguenti metodi:

GetUserFavoritesServiceMethod(userId, requestByUserId)
{
   // Make sure the request is allowed
   // This is the business logic where I make sure the requesting party is authorized to make the request

  // If allowed, call repository method
  var favs = GetUserFavoritesRepositoryMethod();
}

GetUserFavoritesRepositoryMethod(userId)
{
   // Return results
}

Ora, supponiamo di avere altri metodi di servizio che richiedono i preferiti dell'utente. Potrei semplificare eccessivamente il mio esempio, ma qui ho la possibilità di chiamare il metodo di repository o il metodo di servizio esistente.

GetSomeOtherInfoServiceMethod(userId, requestByUserId)
{
   // Call GetUserFavoritesServiceMethod() OR GetUserFavoritesRepositoryMethod()
}

Chiamare GetUserFavoritesServiceMethods() sembra un po 'macchinoso perché potrei già aver eseguito alcune logiche di business in GetSomeOtherInfoServiceMethod() ed eseguire nuovamente la logica di business in GetUserFavoritesServiceMethod() sembra brutto / dispendioso / ecc.

D'altra parte, GetUserFavoritesServiceMethod() potrebbe essere piacevolmente impacchettato per me rispetto a GetUserFavoritesRepositoryMethod() potrebbe non esserlo.

Ancora una volta, ho finito per semplificarlo ma chiaramente aggiungo un po 'di "valore" ai metodi di servizio ea volte voglio approfittare di quel "valore".

Voglio vedere se chiamare un metodo di servizio da un altro metodo di servizio è generalmente disapprovato.

    
posta Sam 05.08.2017 - 22:13
fonte

2 risposte

1

Sei di fronte a un problema di modello di dominio anemico che porta all'incertezza su dove mettere la logica di business "generale" che non è specifico per un caso d'uso.

Esistono due tipi di logica aziendale:

  1. specificazione specifica
  2. usecase non specifico

Guarda le dichiarazioni di Robert C. Martin:

Keynote di OOP 2015 - Robert C. Martin ("Uncle Bob"): Agilità e architettura (26:55 - 28:22)

Robert C. Martin individua la logica aziendale specifica di usecase in oggetti denominati "interattori". Puoi anche dire "casi d'uso". Forse i "servizi" sono strettamente correlati, quindi puoi continuare con quello.

Le entità conterranno logiche di business che non sono specifiche di utilizzo. Dice anche che alcuni sviluppatori li chiamano "oggetti business", quindi lo farei.

Qualunque cosa tu chiami: la logica di business specifica specifica di usecase e usecase sono diverse e devono essere separate se chiedi consiglio. Quindi un oggetto del modello di dominio dovrebbe essere il percorso appropriato per inserire il codice del metodo "getUserFavorites".

Il mio suggerimento è: user.getFavorites();

    
risposta data 06.08.2017 - 00:39
fonte
0

Va bene se sei chiaro sulla dipendenza. Cos'è un servizio qui ma un nome? Potresti iniettare tutti i tipi di dipendenze qui.

public class AddedInfoFavoritesService
{
    private IFavoritesService favService;

    public AddedInfoFavoritesService(IFavoritesService favService)
    {
        this.favService = favService;
    }

    public FavoriteInfo GetSomeOtherInfoServiceMethod()
    {
        var fav = favService.GetUserFavoritesServiceMethod();
    }
}

In realtà non mi interessa i dettagli della ricerca finché IFavoritesService mi fornisce una raccolta di preferiti dell'utente.

Il problema è quando si nasconde la dipendenza dal servizio. Ma questo è il suo problema in generale.

public FavoriteInfo GetSomeOtherInfoServiceMethod()
{
    var favService = new UserFavoritesSerivce(userFavRepoDependency, someOtherDependency);
    var favs = favService.GetUserFavoritesServiceMethod();

    // add info
}

Potrebbe esserci un'intera catena di dipendenze che dovrai includere. Test e cambiamenti futuri qui è più difficile. Mi piacerebbe molto di più sui dettagli della ricerca dato che devo fornire le dipendenze dell'altro servizio.

Se le funzioni sono strettamente correlate, potresti aggiungere l'implementazione del metodo alla stessa classe . Dividi i contratti di interfaccia in modo da poterli testare separatamente o se vuoi limitare l'accesso.

public class FavoritesService : IUserFavoritesService, IAddedInfoService
{
    private IRepository repo;

    // IUserFavoritesService
    public IEnumerable<UserFavorites> GetUserFavoritesServiceMethod()
    {
        var favs = GetFavorites();
    }

    // IAddedInfoService
    public FavoriteInfo GetSomeOtherInfoServiceMethod()
    {
        var favs = GetFavorites();
        // add info
    }

    private IQueryable<UserFavorites> GetFavorites()
    {
        var query = repo.Where(...);
    }
}

Utilizza solo i metodi necessari per limitare l'accesso e semplifica il test dei consumatori con una simulazione più semplice.

public class Consumer
{
    private IAddedInfoService infoService;

    public Consumer(IAddedInfoService infoService)
    {
        this.infoService = infoService;
    }

    public FavoriteInfo GetInfo()
    {
        var info = infoService.GetSomeOtherInfoServiceMethod();
        return info;
    }
}

Se il servizio originale diventa troppo grande, sarà più facile da refactoring poiché a Consumer non importa il IUserFavoritesService del contratto.

Quindi potresti creare una classe base per fornire le funzioni comuni.

public abstract class BaseFavService
{
    protected virtual IQueryable<UserFavorite> GetUserFavorites() {  }
}

public class UserFavorites : BaseFavService, IUserFavorites { }
public class AddedInfoFavoritesService : BaseFavService, IAddedInfoService { }
    
risposta data 05.08.2017 - 23:48
fonte

Leggi altre domande sui tag