This repository demonstrates functional core and imperative shell principles in a simple asset tracking and geofencing API. It uses Rust with a vertical slice architecture, but the approach could also apply to any modern language (like C# or Java).
Here is a full article: Applying Functional Core and Imperative Shell in Practice
- Functional Core: All domain logic lives in pure functions; no external dependencies, no side effects.
- Imperative Shell: Deals with HTTP endpoints, database I/O, and other real-world concerns, calling into the functional core for actual business rules.
- Vertical Slice: Each feature (e.g.,
track_asset) is self-contained. It contains its own domain model, logic, and handler code, making the project easy to extend.
We simulate an asset (like a truck or device) that reports its GPS coordinates. The system checks if the asset is inside or outside a predefined geofence:
- The API endpoint receives the asset’s location.
- The domain logic determines whether it’s inside or outside the boundary.
- The system compares the new status to the old status and updates the database accordingly.
- Rust was chosen for its strong compiler guarantees, which encourage clear boundaries and safe concurrency.
- Actix Web provides the HTTP layer (the shell), while SQLx handles database interactions.
- Vertical slices keep each feature cohesive and avoid scattering domain logic across layers.
- Functional Core ensures the business logic is easy to test without spinning up databases or servers.
- Install Rust: https://rustup.rs
- Clone the repo:
git clone https://github.com/ricofritzsche/func-core-feature-example.git cd func-core-feature-example - Set up your database (e.g., PostgreSQL) and provide the URL via
.envor an environment variable:DATABASE_URL=postgres://user:password@localhost:5432/asset_tracking
- Run migrations automatically on app startup:
The code uses
cargo run
sqlx::migrate!()to apply any pending migrations under./migrations.
- Domain tests (pure logic) run with:
cargo test - Integration tests use
reqwestto hit the/trackendpoint:Make sure the server is running (cargo test --test track_asset_apicargo run) if the test doesn’t spawn it automatically.
Send a location update to the running server:
curl -X POST http://localhost:8080/track \
-H "Content-Type: application/json" \
-d '{"asset_id": "demo-asset", "lat": 40.5, "lon": -73.9}'You should see a JSON response indicating whether the asset Entered, Exited, StayedInside, or StayedOutside the geofence.
Feel free to open issues or submit pull requests, especially if you have ideas to extend the feature set or demonstrate additional use cases.
This project is licensed under the MIT License.
Enjoy experimenting with a functional core and imperative shell in your own domain!