According to recommendations given here, F# modules should correspond to DDD bounded contexts, i.e. subdivisions of a business domain.
The bounded context I'm working on right now has 2 aggregates totalling a dozen of types, plus some rather complex functions/algorithms. If I follow the prescribed style, I end up with a 500+ LOC file, which IMO isn't very readable or navigatable, and it will only grow longer. Unfortunately, it seems F# modules can't be split across multiple files.
Other solutions I've attempted are :
- One module per aggregate. Problem -- if I include the aggregate type in the module, I end up with an inelegant aggregate type name (e.g.
MyAggregate.MyAggregate
), especially when reused in non-F# .NET code. One file with all the types of the bounded context under a particular namespace and another file containing the functions inside a module. Still not satisfying since in F# I can't give the same name to the module and namespace, and functions are not really organized inside the file.
// This gives a "namespace and module named ... both occur in 2 parts of this assembly" error //File1.fs namespace MyProject.Domain.MyBC type MyType = // ... //File2.fs namespace MyProject.Domain module MyBC = //...
I'm still looking for a solution that satisfies the following
- Meaningful organization of code reflecting DDD concepts, so that code lookup feels easy and obvious
- Some degree of compartmentalization, to prevent native access to other unrelated parts of the domain and avoid mistakes
- Low cognitive load to explore a file (meaning no more than a few hundred lines of code per file)
- Simple, non contrived usage from external non-F# .NET code
Have I missed the obvious way of doing it, or does the lack of partial modules + namespace/module collision just make them an awkward scope for DDD?