blob: 79c5d06f62f730f1837d78294272f49e772e7c32 [file] [log] [blame] [view]
# Core and Bindings
This document describes and motivates the design of the netstack into separate
"core" and "bindings" components.
## Architecture
The netstack codebase is split into two crates - the `core` crate, and the
top-level `netstack3` crate. We refer to these as the "core" and bindings"
respectively.
The core is a library crate, and contains most of the logic of the netstack.
However, it is also platform-agnostic. Any client of the core which produces a
binary is known as "bindings" because they bind the core to a particular
platform. Currently, `netstack3` crate, which provides bindings to Fuchsia, is
the only bindings crate in existence.
## Benefits
This section explores some of the major benefits of the core/bindings split.
### "Functional core/imperative shell"
The core/bindings split naturally implements a [functional core/imperative
shell](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell)
design pattern. In this pattern, the bulk of the application logic lives in a
"functional core" which concerns itself with the high-level tasks of the
application. Ideally, the functional core can be unaware of "real world" details
like execution order, communication with the outside world, input validation,
etc. The "imperative shell", on the other hand, concerns itself with all of the
real world details that the functional core ignores.
*NOTE: In the original "functional core/imperative shell" concept, the
functional core is functional in the sense of not having any mutable state. Our
core is not functional in this sense, but our architecture still shares many
properties with that design, so the comparison still makes sense.*
This architecture provides us with a number of benefits:
- High-level testing of the core is easy. Since, to the core, the outside world
simply looks like a trait implementation, mocking out the entire world is
simply a matter of implementing that trait (in particular, the
`EventDispatcher` trait). The core's `testutil::DummyEventDispatcher` serves
this purpose, and allows many of our tests to create an entire mocked
execution environment, execute a sequence of actions, and test to see that the
right events were emitted in response, and in only a few lines of code.
- The core can be largely infallible. It can provide types and methods whose
signatures force the bindings to perform any input validation ahead of time
before calling into the core. This both simplifies the core, and also forces
the best practice of validating external input as early as possible.
- While the core must, at a minimum, enable whatever execution model the
bindings want to use (e.g., if the core's data structures are not thread-safe,
then the bindings cannot execute across multiple threads), it can simply
expose functions and leave it up to the bindings to call those functions at
appropriate times.
### Ease of development
Since the core is platform-agnostic, it doesn't require Fuchsia's full build
system to develop on. In fact, the author of this document usually develops by
using the normal `cargo` tool rather than the Fuchsia-specific `fx` or `fargo`
tools. This unlocks both fast development cycles and the full suite of `cargo`
tooling.
### Platform-agnosticism
With the core being platform-agnostic, it should be possible to port the
netstack to other platforms with relative ease.