Skip to content

Hexagonal Boundaries: core/ and shell/

Principle

Every module separates pure business logic (core/) from infrastructure concerns (shell/). Dependencies always point inward — shell/ depends on core/, never the reverse.

Why This Matters

  1. Testabilitycore/ can be tested without mocking AWS, databases, or network. Business logic tests are fast, reliable, and easy to write.
  2. Portability — if we swap MySQL for PostgreSQL, or SQS for Kafka, only shell/ changes. core/ remains untouched.
  3. Clarity — when reading core/, you understand the business rules. When reading shell/, you understand the infrastructure. They don't mix.
  4. Onboarding — new developers can understand the domain by reading core/ alone without needing to understand AWS services.

The Boundary

core/ contains:

  • Business entities and value objects (SQLAlchemy models define schema, but business rules live in core/)
  • Exception hierarchy that controls processing flow
  • Checkpoint state machines for resumable pipelines
  • Data validation schemas (Pydantic, TypedDict)
  • Pure utility functions (retry decorators, context management)
  • Business orchestration (process.py)

shell/ contains:

  • Database session management (connection pooling, read/write separation)
  • AWS service wrappers (SNS publishing, SQS operations, S3 access)
  • Elasticsearch client and bulk indexing
  • Secrets retrieval from AWS Secrets Manager
  • Domain-specific database operations (CRUD for entities)

handlers/ contains:

  • Lambda entry points that bridge shell → core
  • Event parsing and routing
  • Exception-based flow control (which exceptions trigger which SQS behavior)

The Rule

core/ NEVER imports from shell/
core/ NEVER imports boto3, botocore, or any AWS SDK
core/ NEVER makes network calls, file I/O, or database queries directly

shell/ passes data INTO core/ via function arguments
shell/ creates sessions and passes them to core/ methods
shell/ handles all infrastructure lifecycle (connections, retries, cleanup)

How Sessions Cross the Boundary

The one pragmatic exception: SQLAlchemy sessions are created in shell/ and passed to core/ methods as arguments. This keeps core/ testable (mock the session) while allowing it to express database operations through the ORM.

# shell/ creates the session
with writer_session(npcase_id=case_id) as session:
    # core/ receives it as a parameter
    result = create_exhibit_data(session, import_attributes)

This is the agreed boundary contract. core/ never creates its own sessions.

Ask the Architecture ×

Ask questions about Nextpoint architecture, patterns, rules, or any module. Powered by Claude Opus 4.6.