3 ms·
If Go is a feasible option [1], each service could be structured as its own module, with all of its private data and types placed in packages inside the module'
by shoo 4y ago
If Go is a feasible option [1], each service could be structured as its own module, with all of its private data and types placed in packages inside the module's special internal/ package, which has special support from the compiler:
> internal/ is a special directory name recognised by the go tool which will prevent one package from being imported by another unless both share a common ancestor. Packages within an internal/ directory are therefore said to be internal packages.
https://dave.cheney.net/2019/10/06/use-internal-packages-to-reduce-your-public-api-surface https://dave.cheney.net/2019/10/06/use-internal-packages-to-...
It'd be the responsibility of the owner of each service to opt-in and put private things in their internal/ package, and not expose them as part of the service's module's public interface to other modules.
More generally, if you have a lot of influence over how your services are written and maintained, even for languages and tooling that doesn't give you a built-in way to enforce that external dependencies on private details of other components are not introduced, it might not be that hard to write a custom linter script to analyse all the imports in your project and fail the build if any change introduces a forbidden dependency.
Re: running things in a single process. With Go, suppose you craft a number of services wrapped in their own packages with well-designed public interfaces, and all private details hidden via the compiler-enforced internal/ package mechanism. There are still scenarios where the modularity can be horribly broken, and the implementation details matter, especially if you're running everything on a single machine or in a single process: e.g. suppose the private internals of one service want to leak all of the TCP buffer memory or open all the file handles, that'll likely break the internals of some other service that is competing for the same shared resources. Similarly, suppose some internal code deep in the bowels of one service triggers a panic inside a goroutine that isn't trapped, then the go runtime will react by terminating the whole process, taking down all of your services. The language-level modularity doesn't give you a way to enforce that services running in the same process or in the same machine can't trash shared resources.
[1] Go is a pretty good fit for writing backend web services, and less of a good fit for, say, shipping mobile apps, or prototyping machine learning algorithms.
- lucidguppy 4y agoThis is a great point - and its a widely used language.