A frequent question I get is how to structure a codebase in F#. There is not much about this topic out there on the Internet. So here is how we structure our code:
The above snippet from our solution shows a single sub-system. Our application, an attendance tracking system, is split into several sub-systems: attendance time, employment (everything about employees), organisation (who works where in which role), absence planning (like vacation, or being sick), duty planning, activity time (e.g. booking time on projects), expenses, tool usages, and a couple more.
Each slice follows roughly the same layered structure:
- API: provides end-points for the clients to call
- Facade: provides an interface of the core business functionality to call it from the API or our tests. We distinguish between calls coming from the API (
OrganisationFeatures
) and calls coming from other sub-systems (AdapterFacade
). - Features: the commands and queries that the sub-system provides
- Core domain: models and business logic that enable the features
Because F# uses strict ordering of files and definitions in these files – you can only use what was defined above – the order of the files in the snippet is reversed to the list.
Feedback
All sub-units are deployed together to Azure-WebApps and -Functions. We see no benefit in individual deployment at the moment because we are a single team, and the deployment takes 30 seconds with zero downtime. The coupling due to how our business works, is high as well.
We have a mixed codebase (F# and C#), so the composition is a bit of a wild mix of both. That’s why I don’t want to show it. But my mate @domhelfenstein has made a small sample of how we would probably do it https://github.com/domenichelfenstein/bike-rental-showcase/tree/master…
Composition Root: bike-rental-showcase/FacadesCreator.fs at master · domenichelfenstein/bike-rental-showcase · GitHub
Core assemblies have fewer assembly/Nuget package dependencies, which results in faster compile time. Most tests run against code in Core assemblies. So the TDD cycle is faster because the build takes less time.