mardi 23 août 2016

Business objects and multiple data sources

On my WPF application, I made two separate projects: the UI (with the XAML and the ViewModels) and the "Core" (it has what people call "domain objects" or "business objects" - objects that represent discrete concepts in my use cases). I understand this is a good practice.

But many of my business objects interact with multiple data sources. For example, a Document object might contain data from the database as well as from a file.

And then, some of the things you can "do" with Document - which I implement as methods on Document - involve other resources. For instance, Document.SubmitForProcessing() calls a web service.

So I wrote this:

public class Document
{
    public string Name { get; set; }
    public string FilePath { get; set; }
    public string FileData { get; set; }

    private Document() { }

    public static Document GetByID(int documentID, string databaseConnectionString, string baseFilePath)
    {
        // Using Dapper
        Document newDoc = db.Query<Document>("SELECT Name, FilePath FROM Documents WHERE ID = @pID", new { pID = documentID });

        newDoc.FileData = File.ReadAllText(Path.Combine(basePath, newDoc.FilePath));

        return newDoc;
    }

    public void SubmitForProcessing(IWebService webService)
    {
        webService.ExecuteFoo(this.Name, this.FileData);
    }

    public void DoBusinessStuff()
    {
        this.FileData = this.FileData.Replace("foo", "bar");
    }
}

Needless to say, this got super annoying really fast and it's sort of difficult to write tests against it.

So I read about dependency injection and the repository pattern. But I'm not sure how to do it correctly in this scenario. Do I have separate repository classes for each data source, and then some kind of DocumentFactory or something that accesses the separate repositories and stitches together a Document object? Or is there a simpler way?

My main concern is to make the code test-friendly so I can write some unit tests without mocking up an entire database and filesystem, but also to stop passing a whole smorgasbord of parameters to each and every factory method that I have (e.g. GetByID(int documentID, string databaseConnectionString, string baseFilePath) - my real life ones have over half a dozen parameters like this).

On similar questions, the answers talk about things like SOLID, YAGNI, repositories are for CRUD, etc. I value those principles, but I'm having trouble getting the practical design from them. For instance, the web service isn't really CRUD-y. Do I have a "repository" for it so that I can switch it out during unit tests? What about for the filesystem?

TL;DR - what's wrong with that code?

Guidance appreciated. Thank you!

Aucun commentaire:

Enregistrer un commentaire