Transazione del database e separazione delle preoccupazioni

2

Struttura di applicazione:

Livello aziendale

public interface IOrderDataService 
{
    void Save(Order order);
}

public interface IOrderLineDataService 
{
    void Save(OrderLine orderLine);
}

public class OrderManager
{
    private IOrderDataService _OrderDataService;
    private IOrderLineDataService _OrderLineDataService;

    public void Create(Order order, List<OrderLine> lines)
    {
        _OrderDAtaService.Save(order);

        foreach(var line In lines)
        {
            line.Id = order.Id;
            // some other business logic
            _OrderLineDataService(line);
        }
    }             
}

Il livello dati implementa gli intereface "DataService" dal livello aziendale

public class SqlOrderDataService : IOrderDataService 
{
    void Save(Order order) {};
}

public class SqlOrderLineDataService : IOrderLineDataService 
{
    void Save(OrderLine orderLine) {};
}

Ora desidero che la creazione dell'ordine utilizzi SqlTransaction per la creazione di righe ordine e ordine. Ma ..

Se avvolgo un metodo OrderManaget.Create con SqlTransactionScope - funzionerà, ma il livello aziendale dipenderà dal database sql.

Se creo un metodo wrapper che crei sia Order che OrderLine s. E usa la transazione lì - funzionerà

public interface IOrderManagerDataService 
{
    void Save(OrderLine orderLine, OrderLine lines);
}

Ma in questo caso la logica di business sarà nell'implementazione Sql del livello dati. Che violano una separazione di preoccupazioni e voglio tenerlo sul livello aziendale.

Hai qualche consiglio per questo problema?

    
posta Fabio 06.07.2016 - 13:39
fonte

5 risposte

3

Se vuoi veramente mantenerlo tutto separato, puoi creare la tua interfaccia TransactionScope e utilizzarla nel tuo metodo Create .

L'implementazione di detta interfaccia utilizza quindi SqlTransactionScope per eseguire il lavoro "reale".

    
risposta data 06.07.2016 - 16:19
fonte
2

Ciò di cui hai bisogno è un'unità di lavoro per i tuoi archivi. L'unità di lavoro avrebbe un'interfaccia simile a

public interface IUnitOfWork
{
    ITransaction Start();
    void Commit();
    void RollBack();
}

quindi il tuo metodo di salvataggio su IOrderDataService e IOrderLineDataService richiederebbe un ITransaction , diventando:

public interface IOrderDataService 
{
    void Save(Order order, ITransaction trans);
}

public interface IOrderLineDataService 
{
    void Save(OrderLine orderLine, ITransaction trans);
}

Avendo un'unità di lavoro è possibile evitare di conoscere una transazione SQL e programmare l'interfaccia mentre si è ancora in grado di utilizzare una transazione SQL nell'implementazione specifica.

    
risposta data 06.07.2016 - 18:15
fonte
1

I dati persistenti in un archivio di date (DB o altro) non sono realmente business logic, quindi spingere il SqlTransactionScope il più vicino possibile al livello dati in quanto controlla il modo in cui i dati vengono mantenuti / tradotti. Il tuo ultimo metodo di:

void Save(Order order, List<OrderLine> orderlines)

Questo ha più senso in quanto il SqlTransactionScope sarebbe implementato in quel metodo. Il livello aziendale potrebbe prendere i risultati del salvataggio ed eseguire eventuali mutazioni / trasformazioni / derivazioni secondo necessità.

Si potrebbe anche mantenere i singoli metodi di salvataggio per Order e OrderLines se gli utenti sono autorizzati ad aggiornarli singolarmente se lo desiderano.

    
risposta data 06.07.2016 - 16:37
fonte
1

Se vuoi evitare SqlTransaction puoi utilizzare TransactionScope da System.Transactions . Ciò avrà l'ulteriore vantaggio che qualsiasi sistema che utilizza MSDTC possa utilizzare la stessa transazione. Disaccoppia anche il database dal livello aziendale consentendo al livello aziendale di raggruppare logicamente i dati transazionali.

    
risposta data 06.07.2016 - 16:48
fonte
0

I post qui le mie correnti hanno pensato a questo problema.
Quindi, dopo aver iniziato a implementare il proprio IDatabaseTransaction , mi rendo conto che la radice del problema non è nella transazione, ma nel fatto che il mio livello di database contiene ancora alcune logiche di business (genera alcuni valori predefiniti durante l'inserimento di nuove righe).

Quindi la mia nuova decisione è stata creare e generare tutti i dati necessari sul livello aziendale e quindi passare tali informazioni come parametri all'unico metodo del livello dati in cui il codice verrà eseguito all'interno della transazione

public interface IOrderCreationDataService 
{
    void SaveFullOrder(Order order, List<OrderLine> orderLines);
}

public class SqlOrderCreationDataService : IOrderCreationDataService 
{
    void SaveFullOrder(Order order, List<OrderLine> orderLines) 
    {
        using(var scope = new TransactionScope())
        {
            this.SaveOrder();
            this.SaveorderLines();
        }
    };
}
    
risposta data 15.07.2016 - 11:34
fonte

Leggi altre domande sui tag