I've been trying for some time to use the coding/design principles shown in the seemingly rather insightful, but for a time quite hard to decipher, post here:
How should a model be structured in MVC?
by tereško (it is/was hard to decipher because it seems to be heavily oriented toward a Web/Internet programming audience, and that is not a field with which I had much familiarity - most of my experience centers on desktop and mobile devices i.e. "the whole program runs on one device" paradigm). And one thing that has now come to bug me is the relationship between the Domain Objects, as he describes them, and the Services, also as he describes. I've later found that this appears to be taking ideas from a broader area called Domain-Driven Design (DDD), and while there seem to be a fair bit of resources on this topic, a lot of it is expensive books I cannot afford to pay for, so I have to try to wrack myself with more limited internet-based articles to get a feel for what all and everything that is being talked about. I'm not necessarily interested in perusing a full and rigorous "DDD" setup, but rather just the broad strokes of the pattern that tereško outlines, in a consistent way particularly in light of other "good code" principles like SOLID.
And that's the one that in particular I seem to have run into a bit of difficulty with in the following case. Suppose you have an app that functions as a diary (I've made such, but I'm actually thinking of something else here). As I get it from the above, an object called "Diary" might live in the "Domain Objects" portion of the program and do and represent just what you think it would. Likewise, in the UI layer, where we may have the "View" and "Controller" arms of MVC, or maybe "View" and "ViewModel" if we're using MVVM or whatever else is appropriate to the MV* flavor we want. The trick though is particularly the whole "interface" business suggested by SOLID - in fact, 4 of the 5 letters, all the ones after the "S"! This part seems clear enough (using a Java-style code):
Consider the "New Diary" command on the user interface. This button will send an event to an MVC Controller, which then must use a Model Service interface, presumably like this to satisfy ISP of SOLID (don't have available for calling more than you need):
public interface IDiaryCreatorService {
void createDiary(String name);
}
Model Layer contains an implementation. This is where the trick is. Presumably, the implementor is going to call a Factory of some kind that will produce new Diary domain objects, and will be dependency-injected into the Controller:
// in Model
public class DiaryCreatorService implements IDiaryCreatorService {
@Inject private IDiaryFactory mDiaryFactory;
@Inject private IDiaryRetainer mDiaryRetainer; // implemented in Data layer
public void createDiary(String name) {
Diary diary = mDiaryFactory.create(name); // concrete Diary is exposed to service!
mDiaryRetainer.retain(diary);
}
}
Note the commented bit. Does this seemingly very reasonable design - note also that while I imagine that IDiaryRetainer
is to be implemented as one head of a DiaryRepository
down in the Data Layer, I have made DiaryCreatorService
not depend on a full IDiaryRepository
interface, again, because of the "I" in SOLID as applied to the caller, as this caller dose not need to know of, say, any Retrieve
method it may have - still end up as "bad design" because in some sense "DiaryCreatorService" depends on, since it "sees", the concrete class Diary
which is the Domain Object? Yet it seems there's no other way to make this work, because that has to get - in the "job description" of a Service
as a "higher-level Domain Object", from the factory into the database or whatever backing collection the rest of the program draws on to do further operations!
Now I can figure some ways around this. One would be to simply @Inject
a different interface that replaces the Factory and which also can receive a suitable receptacle. This object actually directly calls the construction on the Diary, as in a Factory, and then pushes it to the receptacle, but that also seems now kind of ugly because it feels kind of SRP-problematic.
The other way is to have the Factory
interface not return a concrete class, but instead only return some kind of opaque ADiaryStub
or something that Data Layer then receives through IDiaryRetainer
and then downcasts as need be, but that again, seems kinda funny because of the downcast which doesn't seem like it should be necessary here.
What am I missing? How strict should one interpret or not the "knowledge hiding" aspects of the OO principles for these kind of situations?