Skip to main content

Modules, Crates, Workspaces, and Project Shape

Watch First

Why This Matters

Rust projects become hard to change when every module can see everything. Good project shape makes dependencies obvious and keeps the domain from leaking into HTTP, SQL, config, and worker concerns.

Architecture is not folders. Architecture is knowing what should not know about what.

What You Will Build

Convert rust-lab into a workspace with clean crates and an ADR explaining the chosen architecture.

Concept

Use crate boundaries when they clarify ownership of concepts. Use module visibility to keep internal details internal.

Rust Pattern

Recommended starting layout:

rust-lab/
Cargo.toml
crates/
api/
domain/
application/
infrastructure/
workers/
shared/
docs/
adr/
migrations/
tests/

Keep the workspace root simple:

[workspace]
resolver = "3"
members = [
"crates/api",
"crates/domain",
"crates/application",
"crates/infrastructure",
"crates/workers",
"crates/shared",
]

Practice

Keep this mistake out of your first implementation.

Do not create a workspace on day one if the project is still a tiny CLI. Split when the boundaries are starting to matter.

Also avoid making everything pub. Visibility is a design tool:

pub(crate) mod parser;
mod normalize;
pub mod commands;

Keep these concrete mistakes out of your work.

  • Generating too many crates too early.
  • Making every module public.
  • Putting config reads deep inside business logic.
  • Creating feature flags before there is a tested need.

Use this sequence. Do not move to the next row until you have produced the artifact in the right column.

StepFocusArtifact
Binary vs library cratemain.rs, lib.rs, reusable codeSplit CLI from library
Modules and visibilitymod, pub, pub(crate)Private internals
Cargo workspacesShared lockfile, shared build outputWorkspace root
Recommended workspaceDomain/application/infrastructure/interfaces/workersFolder structure
ConfigurationTyped settings and .env.exampleConfig loader
Feature flagsUseful toggles vs complexityFeature decision note
ADRsRecord architecture decisionsdocs/adr/0001-architecture.md

Build this now. Keep each change small enough that you can run cargo check, cargo test, and inspect the diff.

Move validation and normalization logic out of the CLI entry point into a library crate. Keep the CLI responsible only for:

  • reading command-line arguments,
  • reading files,
  • calling library functions,
  • printing results,
  • mapping errors to process exit behavior.

After your own attempt, use another reviewer or an AI tool as a second pass. Accept a suggestion only when you can explain why it preserves the lesson design.

Ask AI to restructure the project into a workspace. Reject the version if:

  • domain logic imports the HTTP framework,
  • infrastructure types leak into public domain APIs,
  • every module is public,
  • the workspace adds crates that have no responsibility yet.

You can move on when these statements are true.

  • Can a contributor tell where new code belongs?
  • Does the domain avoid framework imports?
  • Are public APIs deliberately small?
  • Is configuration passed in from the edge?
  • Is there an ADR for major choices?
  • Did splitting crates make dependencies clearer?

Curated Resources

Next Step

Continue to Smart Pointers, Shared State, and Concurrency Basics.