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