Skip to content

Autowire use-case DI, map chat-listing state, keyword-only use cases - #2

Merged
lesnik512 merged 1 commit into
mainfrom
refactor-di-wiring-and-chat-listing
Aug 21, 2026
Merged

Autowire use-case DI, map chat-listing state, keyword-only use cases#2
lesnik512 merged 1 commit into
mainfrom
refactor-di-wiring-and-chat-listing

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Four wiring simplifications with no change to the HTTP contract. Rationale and
rejected alternatives live in
planning/changes/2026-08-21.02-di-wiring-and-chat-listing.md.

  • Autowired DI. ModernDIPlugin(di_container, autowired_groups=[ioc.UseCases])
    replaces the twelve-entry dependencies= dict and its imports in build_app.
    Database and Repositories stay unwired on purpose.
  • Chat listing. ChatsTable maps unread_count (query_expression, filled by
    with_expression) and last_message (viewonly relationship, one selectinload),
    so list_for_user returns entities whose attribute names already match the
    schema. ChatListRow, ChatListItem.from_row and the manual WHERE id IN (...)
    preview batch are gone; the endpoint is one Chats.from_models(...).
  • Keyword-only use cases. All ten __call__s, e.g.
    create_chat_use_case(actor=request.user, data=data).
  • AuthedRequest. Authenticated handlers annotate the request with the alias
    instead of restating litestar.Request[UsersTable, Any, Any].

Notes for review

  • Response JSON is unchanged — tests/api/test_chat_listing_api.py needed no edits.
  • list_for_user runs populate_existing=True: sessions are expire_on_commit=False,
    so an identity-mapped chat would otherwise keep the unread_count it was first
    loaded with, and a second listing in the same session would return stale numbers.
    Safe only because the query is read-only.
  • last_message_id carries no ForeignKey (it would close a cycle with
    messages.chat_id), so the relationship annotates the join column orm.foreign()
    and folds the soft-delete guard into its primaryjoin.
  • No DDL, so no migration. alembic check reports the same pre-existing
    ck_chats_chattype diff as main and nothing else.

Verification

  • just test — 108 passed, 100% coverage.
  • just lint — clean.
  • just check-planning — OK.

Four wiring simplifications, no change to the HTTP contract.

ModernDIPlugin now autowires ioc.UseCases, so build_app's twelve-entry
dependencies= dict and its use-case imports are gone; a new use case needs
only its provider. Database and Repositories stay unwired - a route handler
has no business resolving a session, transaction or repository.

ChatsTable maps unread_count (query_expression, filled by with_expression)
and last_message (viewonly relationship, one selectinload) itself, so
list_for_user returns plain entities whose attribute names already match the
response schema. ChatListRow and ChatListItem.from_row both disappear and
the listing endpoint is a single from_models call. last_message_id carries
no ForeignKey, so the relationship annotates the join column foreign() and
folds the soft-delete guard into its primaryjoin; list_for_user runs
populate_existing because sessions are expire_on_commit=False. No DDL, so
no migration.

Every use case __call__ is keyword-only, and authenticated handlers annotate
the request app.api.auth.AuthedRequest rather than restating
litestar.Request[UsersTable, Any, Any].
@lesnik512
lesnik512 merged commit d974126 into main Aug 21, 2026
2 checks passed
@lesnik512
lesnik512 deleted the refactor-di-wiring-and-chat-listing branch August 21, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant