Domanda

Stavo guardando il differenze tra POCO e DTO (Sembra che POCO'S sono DTO con comportamento (metodi?)) e sono imbattuto in questo articolo da Martin Fowler sul modello di dominio anemico .

Con la mancanza di comprensione, penso di aver creato uno di questi modelli di dominio anemici.

In una delle mie applicazioni ho i miei entità del dominio di business definite in un dll 'dto'. Hanno un sacco di oggetti con getter e setter di di e non molto altro. Il mio codice logica di business (popolano, calcolare) è in un altro dll 'BLL', e il mio codice di accesso ai dati è in una DLL 'dal'. 'Best practice' ho pensato.

Quindi in genere creo un dto in questo modo:

dto.BusinessObject bo = new dto.BusinessObject(...)

e passarlo allo strato BLL questo modo:

bll.BusinessObject.Populate(bo);

che a sua volta, esegue una logica e lo passa allo strato dal modo:

dal.BusinessObject.Populate(bo);

Dalla mia comprensione, per fare le mie DTO in POCO di ho bisogno di fare la parte logica di business e comportamenti (metodi) dell'oggetto. Così, invece di codice di cui sopra è più simile a:

poco.BusinessObject bo = new poco.BusinessObject(...)
bo.Populate();

es. Chiedo il metodo sull'oggetto piuttosto che passare l'oggetto al metodo.

La mia domanda è - come posso fare questo e mantenere la 'best practice' stratificazione di preoccupazioni (DLL separata, ecc ...). Non chiamare il metodo sull'oggetto significa che il metodo deve essere definito nell'oggetto?

Si prega di aiutare la mia confusione.

È stato utile?

Soluzione

In genere, non si vuole introdurre la persistenza nei vostri oggetti del dominio, in quanto non fa parte di quel modello di business (un aereo non costruisce per sé, vola passeggeri / merci da un luogo ad un altro). Si dovrebbe usare la Pattern Repository, una quadro ORM , o qualche altro modello di accesso ai dati per gestire l'archiviazione persistente e retreival di stato di un oggetto .

Se il modello di dominio anemico entra in gioco è quando si sta facendo cose come questa:

IAirplaneService service = ...;
Airplane plane = ...;
service.FlyAirplaneToAirport(plane, "IAD");

In questo caso, la gestione dello stato del velivolo (che si tratti di volare, dove sta, qual è l'orario di partenza / aeroporto, qual è l'orario di arrivo / aeroporto, qual è il piano di volo, ecc) è delegata a qualcosa di esterno al aereo ... l'istanza AirplaneService.

Un modo POCO di attuazione del presente sarebbe quello di progettare la vostra interfaccia in questo modo:

Airplane plane = ...;
plane.FlyToAirport("IAD");

Questo è più rilevabile, dal momento che gli sviluppatori sanno dove guardare per fare un aereo volare (basta dire l'aereo per farlo). Esso permette anche di garantire che lo stato è solo gestite internamente. È quindi possibile fare cose come posizione corrente di sola lettura, e garantire che è cambiato solo in un posto. Con un oggetto dominio anemico, poiché lo stato è impostato esternamente, scoprendo in cui stato viene modificato diventa sempre più difficile la scala aumenta il tuo dominio.

Altri suggerimenti

Credo che il modo migliore per chiarire questo è per definizione:

DTO: Trasferimento dati Oggetti:

Servono solo per il trasporto di dati tipicamente tra livello di presentazione e il livello di servizio. Niente di meno o più. Generalmente è implementato come classe con Ottiene e imposta.

public class ClientDTO
{
    public long Id {get;set;}
    public string Name {get;set;}
}

BO: Business Objects:

Gli oggetti business rappresenta gli elementi di business e naturalmente la migliore pratica dice che dovrebbero contenere la logica di business anche. Come detto da Michael Meadows, è anche buona pratica per isolare l'accesso ai dati da questi oggetti.

public class Client
{
    private long _id;
    public long Id 
    { 
        get { return _id; }
        protected set { _id = value; } 
    }
    protected Client() { }
    public Client(string name)
    {
        this.Name = name;    
    }
    private string _name;
    public string Name
    {
        get { return _name; }
        set 
        {   // Notice that there is business logic inside (name existence checking)
            // Persistence is isolated through the IClientDAO interface and a factory
            IClientDAO clientDAO = DAOFactory.Instance.Get<IClientDAO>();
            if (clientDAO.ExistsClientByName(value))
            {
                throw new ApplicationException("Another client with same name exists.");
            }
            _name = value;
        }
    }
    public void CheckIfCanBeRemoved()
    {
        // Check if there are sales associated to client
        if ( DAOFactory.Instance.GetDAO<ISaleDAO>().ExistsSalesFor(this) )
        {
            string msg = "Client can not be removed, there are sales associated to him/her.";
            throw new ApplicationException(msg);
        }
    }
}

Servizio o classe di applicazione Queste classi rappresentano l'interazione tra l'utente e il sistema e faranno uso di entrambi ClientDTO e Client.

public class ClientRegistration
{
    public void Insert(ClientDTO dto)
    {
        Client client = new Client(dto.Id,dto.Name); /// <-- Business logic inside the constructor
        DAOFactory.Instance.Save(client);        
    }
    public void Modify(ClientDTO dto)
    {
        Client client = DAOFactory.Instance.Get<Client>(dto.Id);
        client.Name = dto.Name;  // <--- Business logic inside the Name property
        DAOFactory.Instance.Save(client);
    }
    public void Remove(ClientDTO dto)
    {
        Client client = DAOFactory.Instance.Get<Client>(dto.Id);
        client.CheckIfCanBeRemoved() // <--- Business logic here
        DAOFactory.Instance.Remove(client);
    }
    public ClientDTO Retrieve(string name)
    {
        Client client = DAOFactory.Instance.Get<IClientDAO>().FindByName(name);
        if (client == null) { throw new ApplicationException("Client not found."); }
        ClientDTO dto = new ClientDTO()
        {
            Id = client.Id,
            Name = client.Name
        }
    }
}

Personalmente non trovo quei modelli di dominio anemici così male; Mi piace molto l'idea di avere oggetti di dominio che rappresentano solo i dati, non il comportamento. Credo che l'aspetto negativo di questo approccio è la reperibilità del codice; è necessario sapere quali azioni disponibili per usarli. Un modo per aggirare questo e ancora mantenere il codice comportamento disaccoppiato dal modello è quello di introdurre interfacce per il comportamento:

interface ISomeDomainObjectBehaviour
{
    SomeDomainObject Get(int Id);
    void Save(SomeDomainObject data);
    void Delete(int Id);
}

class SomeDomainObjectSqlBehaviour : ISomeDomainObjectBehaviour
{
    SomeDomainObject ISomeDomainObjectBehaviour.Get(int Id)
    {
        // code to get object from database
    }

    void ISomeDomainObjectBehaviour.Save(SomeDomainObject data)
    {
        // code to store object in database
    }

    void ISomeDomainObjectBehaviour.Delete(int Id)
    {
        // code to remove object from database
    }
}
class SomeDomainObject
{
    private ISomeDomainObjectBehaviour _behaviour = null;
    public SomeDomainObject(ISomeDomainObjectBehaviour behaviour)
    {

    }

    public int Id { get; set; }
    public string Name { get; set; }
    public int Size { get; set; }


    public void Save()
    {
        if (_behaviour != null)
        {
            _behaviour.Save(this);
        }
    }

    // add methods for getting, deleting, ...

}

In questo modo è possibile mantenere l'attuazione comportamento separato dal modello. L'utilizzo di implementazioni di interfacce che vengono iniettati nel modello rende anche il codice piuttosto facile per testare, dal momento che si può facilmente prendere in giro il comportamento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top