In quale livello dovrei implementare l'analisi dei file?

3

in una semplice architettura multistrato, in cui layer devo implementare qualcosa come il file di analisi. Ad esempio: ho un file e devo estrarre informazioni specifiche in un oggetto. Penso che questa roba sia l'accesso ai dati, perché il livello aziendale ha bisogno di dati. Come e da dove provengono i dati non è il punto. sei d'accordo?

    
posta Marcel Hoffmann 09.10.2015 - 05:56
fonte

6 risposte

5

Consiglierei di smettere di pensare in termini di "livelli" e iniziare a pensare in termini di "moduli". In questo momento, il tuo livello di accesso ai dati contiene solo il modulo di accesso al database. Quindi, proprio accanto ad esso, metterebbe il modulo di accesso ai file. Questo modulo si troverebbe nello stesso livello del modulo di accesso al database in termini di quali moduli può fare riferimento e quali moduli possono fare riferimento a esso. Ma probabilmente sarebbe una libreria separata, proprio come il modulo di accesso al database è la sua libreria (o lo spero strongmente).

Un caso ancora migliore sarebbe se tu avessi un'astrazione per salvare e caricare dati aziendali. Il livello di accesso ai dati (e i relativi moduli inclusi) conterrebbe quindi le implementazioni di questa astrazione. Quindi avresti SaveDataInDatabase , SaveDataInFileX e SaveDataInFileY classi. Il codice aziendale utilizzerà quindi questa astrazione senza sapere o preoccuparsi di come i dati verranno mantenuti.

    
risposta data 09.10.2015 - 09:22
fonte
1

Da ciò che descrivi a livello base, sei:

  1. Estrazione di dati da una memoria persistente

  2. Analizzalo in una sorta di formato generico

  3. Mapping su oggetti e proprietà

Il Pattern del repository risolve questo problema. Comprende tre tipi principali di classi:

  1. La classe "Repository", che si occupa solo del modello a oggetti, ad es. le tue "classi di entità" o "modello di dominio" nella tua applicazione. Può fornire sia l'accesso in lettura che in scrittura per questi dati

  2. La classe "Gateway", che è responsabile della lettura e scrittura dei dati in una memoria permanente, sia essa un database, un servizio web o un file. Come vedremo in seguito, è qui che dovrebbe esserci l'analisi dei file.

  3. La classe "Factory", che crea istanze del tuo modello di dominio e mappa i dati dai dati generici, il gateway ritorna al tuo modello di dominio.

La chiave di questo modello è l'implementazione di interfacce per ogni tipo di classe. Esaminiamo come un blog potrebbe essere modellato e mantenuto in questo modo.

Modello e interfacce del dominio pattern di repository

Iniziamo con il modello di dominio:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

Niente di troppo elegante. Successivamente, dobbiamo definire le nostre interfacce, prima definendo l'interfaccia per il repository del blog.

public interface IBlogRepository
{
    Blog Find(int id);
    void Create(Blog blog);
    void Update(Blog blog);
    void Delete(Blog blog);
}

L'interfaccia IBlogRepository si concentra principalmente sul tuo modello di dominio senza menzionare il modo in cui la persistenza viene effettivamente raggiunta. Ciò fornisce uno strato di disaccoppiamento per il resto della tua applicazione.

Successivamente, l'interfaccia del gateway del blog consente al repository di preformare le operazioni CRUD specifiche (Crea, Leggi, Aggiorna, Elimina) nel tuo archivio dati.

public interface IBlogGateway
{
    IList<IDictionary<string, object>> Load();
    void Create(int id, string name, string description);
    void Update(int id, string name, string description);
    void Delete(int id);
}

È meglio mantenere il "gateway" privo di qualsiasi conoscenza sul modello di dominio, in quanto ciò garantirà la massima flessibilità per modificare il modello di dominio con effetti limitati sul "gateway" e per modificare il "gateway" con un impatto limitato sul tuo modello di dominio.

Il metodo Load restituisce solo un elenco di oggetti del dizionario, un formato generico e piacevole per i dati.

L'ultima interfaccia è l'oggetto factory del blog responsabile della creazione di istanze del tuo modello di dominio e dell'associazione dei dati generici agli oggetti Blog .

public interface IBlogFactory
{
    Blog CreateInstance(IDictionary<string, object> data);
    void UpdateInstance(IDictionary<string, object> data, Blog instance);
}

Classi concrete del modello di deposito

Ora che abbiamo definito le nostre interfacce, iniziamo creando una classe BlogRepository che implementa l'interfaccia IBlogRepository :

public class BlogRepository : IBlogRepository
{
    private IBlogGateway gateway;
    private IBlogFactory factory;
    private IList<Blog> cache;

    private IList<Blog> Cache
    {
        get
        {
            if (cache == null)
            {
                var data = gateway.Load();

                cache = new List<Blog>();

                foreach (var record in data)
                {
                    cache.Add(factory.CreateInstance(record));
                }
            }

            return cache;
        }
    }

    public BlogRepository(IBlogGateway gateway = null, IBlogFactory factory = null)
    {
        this.gateway = gateway ?? new BlogFileGateway(@"C:\Path\To\Blogs.txt");
        this.factory = factory ?? new BlogFactory();
    }

    public Blog Find(int id)
    {
        Cache.SingleOrDefault(b => b.Id == id);
    }

    public void Create(Blog blog)
    {
        Cache.Add(blog);
        gateway.Create(blog.Id, blog.Name, blog.Description);
    }

    public void Update(Blog blog)
    {
        gateway.Update(blog.Id, blog.Name, blog.Description);
    }

    public void Delete(Blog blog)
    {
        Cache.RemoveAll(b => b.Id == blog.Id);
        gateway.Delete(blog.Id);
    }
}

Ora possiamo iniziare a vedere come ogni pezzo si adatta. Il costruttore di BlogRepository ti consente di passare il tuo gateway e l'oggetto factory, ricadendo su un'implementazione predefinita di tua scelta.

Per trovare un singolo Blog , il repository carica prima tutti i dati dal gateway e li associa agli oggetti usando la fabbrica. Il metodo Find restituisce quindi il primo Blog che corrisponde all'Id. Certo, potresti voler creare un metodo sull'oggetto "gateway" per restituire un singolo record, ma il repository è anche un buon posto per implementare il caching.

Ora entriamo nella parte che ti interessa: analizzare il file.

public class BlogFileGateway : IBlogGateway
{
    private string filePath;

    public BlogFileGateway(string filePath)
    {
        this.filePath = filePath;
    }

    public IList<IDictionary<string, object>> Load()
    {
        // Read entire file and return the parsed data
    }

    public void Create(int id, string name, string description)
    {
        // Append the new data to the file
    }


    public void Update(int id, string name, string description)
    {
        // Update the correct row within the file
    }


    public void Delete(int id)
    {
        // Delete the correct row within the file
    }
}

Il BlogFileGateway implementa l'interfaccia IBlogGateway . Qui puoi aprire, leggere e analizzare il file. Se disponi di un formato di file generico, le tue classi "gateway" potrebbero creare un'istanza di un'altra classe responsabile dell'analisi di ciascun record nel file.

E solo per buona misura, la classe blog factory:

public class BlogFactory : IBlogFactory
{
    public Blog CreateInstance(IDictionary<string, object> data)
    {
        if (data == null)
            return null;

        Blog instance = new Blog();

        UpdateInstance(data, instance);

        return blog;
    }

    public void UpdateInstance(IDictionary<string, object> data, Blog instance)
    {
        instance.Id = int.Parse(data["id"].ToString());
        instance.Name = data["name"].ToString();
        instance.Description = data["description"].ToString();
    }
}

Perché il modello del repository funziona meglio

Poiché disaccoppia l'accesso ai dati e la mappatura dal tuo modello di dominio, sei libero di iniziare con l'archiviazione dei file. Passare allo storage del database implicherebbe la scrittura di una nuova classe gateway per accedere al database e una nuova classe factory per mappare i dati dal database al modello di dominio Blog . Se in seguito si decide di passare a un'architettura orientata ai servizi, è necessario solo scrivere un altro gateway per chiamare il servizio Web e un'altra fabbrica. È possibile modificare o cambiare radicalmente i meccanismi di archiviazione senza modificare le parti principali della propria applicazione.

@BartVanIngenSchenau ha detto nel suo risposta :

For other kinds of data access (data exchange with third parties, reading/writing files, etc.) those architectures have the concept of Services to perform the transportation of the data and to transform it between the external format and something that the business layer can work with.

(sottolineatura mia)

Il vantaggio dell'utilizzo del modello di repository è che il codice che chiama un servizio web non deve vivere nel livello generico e spesso abusato della tua applicazione chiamata "servizi". La chiamata al servizio web avviene nel gateway.

Esempio di utilizzo

Trova un blog, modifica un valore e aggiornalo:

IBlogRepository repository = new BlogRepository();

Blog blog = repository.Find(3);

blog.Description = "Black and white, and read all over";

repository.Update(blog);

In seguito, è necessario utilizzare un database MySQL per scrivere le classi BlogMySqlGateway e BlogMySqlFactory , quindi modificare una riga di codice:

IBlogRepository repository = new BlogRepository(new BlogMySqlGateway(), new BlogMySqlFactory());

Blog blog = repository.Find(3);

blog.Description = "Black and white, and read all over";

repository.Update(blog);

Niente altro deve cambiare. In seguito, decidi che tutto ciò ha bisogno di vivere in "The Cloud":

IBlogRepository repository = new BlogRepository(new BlogWebServiceGateway(), new BlogWebServiceFactory());

Blog blog = repository.Find(3);

blog.Description = "Black and white, and read all over";

repository.Update(blog);

Nient'altro deve cambiare.

    
risposta data 09.10.2015 - 16:22
fonte
0

Nell'analisi dei dati da un file, puoi farlo in UserServicesLayer , se vuoi manipolare quei dati che hanno bisogno di una qualsiasi condizione logica, è il momento di chiamare BusinessLayer e poi fare tutte le cose logiche all'interno di livello, il tuo DataAccessLayer è come il tuo bridge per accedere ai dati dal database, quindi non è consigliabile eseguire l'estrazione dei dati all'interno di DataAccesLayer .

Spero che questo aiuti.

    
risposta data 09.10.2015 - 06:30
fonte
0

Nella maggior parte delle architetture multi-livello, il DAL (Data Access Layer) è riservato per il trasporto dei dati da e verso l'archivio di backup primario dell'applicazione (di solito un database).
Per altri tipi di accesso ai dati (scambio di dati con terze parti, lettura / scrittura di file, ecc.) Queste architetture hanno il concetto di Servizi per eseguire il trasporto dei dati e trasformarli tra il formato esterno e qualcosa con cui il livello aziendale può lavorare.

Quindi, nella tua architettura, la lettura e l'analisi dei file andrebbero in un servizio, di solito chiamato in base al tipo di file o dati che elabora. Ad esempio un XMLImportService .

    
risposta data 09.10.2015 - 08:29
fonte
0

I think that this stuff is data access, because the business layer need data. How and where the data come from is not the point. Are your agree?

Sto suddividendo queste cose in due livelli in realtà.

Le routine di analisi dei file generici ( GenericCSVParser ad esempio) vanno al livello Infrastruttura perché non hanno nulla a che fare con la business logic dell'applicazione. È solo uno strumento di analisi dei file che potrebbe far parte del framework stesso.

I parser di file specializzati ( InvoiceCSVFileParser per esempio) di solito compongono o ereditano da quelli generici e appartengono al livello della logica aziendale.

    
risposta data 09.10.2015 - 09:22
fonte
0

In poche parole, implementa le tue esigenze di analisi come modulo o libreria indipendente dal livello. useresti questa libreria nel livello che ne ha bisogno.

L'implementazione dovrebbe accettare e lavorare con un stream indipendente dal dispositivo (astratto / base o interfaccia) o un string (se non ci si aspetta che i dati di input siano troppo grandi, altrimenti potresti incorrere in problemi di scalabilità in seguito).

E a proposito del livello, sì, nel tuo caso sembra che la funzionalità verrebbe applicata all'accesso ai dati. Ma questo non deve sempre essere il caso. Accade spesso anche che sia necessaria tale funzionalità di analisi nel livello dell'interfaccia utente in cui gli utenti immettono o caricano i dati, prima di poter popolare gli oggetti di business e archiviarli nel database nel formato richiesto.

    
risposta data 09.10.2015 - 16:42
fonte

Leggi altre domande sui tag