c# - Business objects and multiple data sources -
on wpf application, made 2 separate projects: ui (with xaml , viewmodels) , "core" (it has people call "domain objects" or "business objects" - objects represent discrete concepts in use cases). understand practice.
but many of business objects interact multiple data sources. example, document
object might contain data database file.
and then, of things can "do" document
- implement methods on document
- involve other resources. instance, document.submitforprocessing()
calls web service.
so 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 documents 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 say, got super annoying fast , it's sort of difficult write tests against it.
so read dependency injection , repository pattern. i'm not sure how correctly in scenario. have separate repository classes each data source, , kind of documentfactory
or accesses separate repositories , stitches document object? or there simpler way?
my main concern make code test-friendly can write unit tests without mocking entire database , filesystem, stop passing whole smorgasbord of parameters each , every factory method have (e.g. getbyid(int documentid, string databaseconnectionstring, string basefilepath)
- real life ones have on half dozen parameters this).
on similar questions, answers talk things solid, yagni, repositories crud, etc. value principles, i'm having trouble getting practical design them. instance, web service isn't crud-y. have "repository" can switch out during unit tests? filesystem?
tl;dr - what's wrong code?
guidance appreciated. thank you!
static methods , i/o functions naturally hard test if not use advanced unit test tools (for example stubs: https://msdn.microsoft.com/en-us/library/ff798446.aspx). problem facing have static method calling 2 i/o functions. goal decouple them.
the first thing refactor getbyid method factory class. can create factory class passing database , file system i/o implementation interfaces. advantages of using interfaces allows mock i/o behavior. simple interfaces did below, can implement them in test code without mocking. in way, can isolate business logic of getbyid method factory class, , not bother testing i/o itself, done database provider , win32 api. need.
class document { public string filedata { get; set; } public string filerelativepath { get; set; } } interface idocumentrepository { document get(int id); } abstract class documentfactory { public abstract document create(int docid); } interface ifilestore { string read(string filename); } class concretedocumentfactory : documentfactory { private idocumentrepository _db; private ifilestore _filestore; public concretedocumentfactory(idocumentrepository db, ifilestore filestore) { _db = db; _filestore = filestore; } public override document create(int docid) { document newdoc = _db.get(docid); newdoc.filedata = _filestore.read(newdoc.filerelativepath); return newdoc; } } /////// test code below [testfixture] class testclass { class testfriendlyfilestore : ifilestore { public string read(string filename) { if (filename == "sample.txt") return "some file content"; throw new exception("not file name."); } } class testfriendlydocrepo : idocumentrepository { public document get(int id) { if (id != 999) return new document() {filerelativepath = "sample.txt"}; throw new exception("not id."); } } [test] public void test() { var concretedocfactory = new concretedocumentfactory(new testfriendlydocrepo(), new testfriendlyfilestore()); var doc = concretedocfactory.create(999); assert.areequal(doc.filedata == "some file content") } }
Comments
Post a Comment