|
Yes, a good project/module system helps, but is no panacea. As TFA said: "As a result of having only conventions, there is no strong insurance against a developer using the dependencies directly, instead of going through the ports, or adding a new dependencies without the associated port (it happens, we all have seen it)." As long as you have some of the same developers working on multiple layers of the application's architecture, it's easy to accidentally reintroduce violations of the layer boundaries, even if you originally wrote the application using clean, well-designed interfaces. Say project Layer2Implementation depends on project Layer1Interface, as it should, but also depends on Layer1Implementation, possibly due to a transitive dependency that's not immediately obvious. But the build system and IDE know the project dependency is there, and now it's all too easy to refer to a Layer1Implementation internal helper function or have one suggested to you via autocomplete, even if you didn't include it in your top-of-file "open" declarations. And because you were just fixing up something else in Layer 1 an hour ago, you think to yourself "aha, I know that function, it's useful!" and plug it right in to your new Layer 2 code. This may be why some languages are so anal about requiring explicit public/private/internal accessibility modifiers on everything, but those are highly susceptible to code rot anyway. First the real conceptual interface ends up scattered among a bunch of different files. Then under time pressure, more and more things get mindlessly marked public just so a developer can get a feature into an easily testable state. Module interface files are a pain too if they're optional (as in, say, F#). You really want to have one tangible, named entity that groups together the functionality you actually want to expose to other layers. You want to make it impossible to refer to things you shouldn't be referring to, without making accessibility hygiene a maintenance burden in its own right. I feel like ML-style modules and abstract data types are great for this, although I've never written a sizable application in any ML dialect. The new static interface stuff in .Net is a nice step in that direction though. |