from typing import Any, Callable, Optional, Sequence, Type from fastmsa.core import ( AbstractRepository, AbstractUnitOfWork, Aggregate, AggregateReposMap, ) from fastmsa.logging import get_logger from fastmsa.orm import Session, SessionMaker, get_sessionmaker from fastmsa.repo import SqlAlchemyRepository RepoMakerFunc = Callable[[Session], AbstractRepository] RepoMakerDict = dict[Type[Aggregate], RepoMakerFunc] logger = get_logger("fastmsa.uow") class SqlAlchemyUnitOfWork(AbstractUnitOfWork): # type: ignore """``SqlAlchemy`` ORM을 이용한 UnitOfWork 패턴 구현입니다.""" # pylint: disable=super-init-not-called def __init__( self, agg_classes: Sequence[Type[Aggregate]], get_session: Optional[SessionMaker] = None, repo_maker: Optional[RepoMakerDict] = None, ) -> None: """``SqlAlchemy`` 기반의 UoW를 초기화합니다.""" super().__init__() self.agg_classes = agg_classes
from fastmsa.logging import get_logger from fastmsa.utils import Fore, Style, bold, cwd, fg, scan_resource_dir YELLOW, CYAN, RED, GREEN, WHITE = ( Fore.YELLOW, Fore.CYAN, Fore.RED, Fore.GREEN, Fore.WHITE, ) WHITE_EX, CYAN_EX = Fore.LIGHTWHITE_EX, Fore.LIGHTCYAN_EX BRIGHT, RESET_ALL = Style.BRIGHT, Style.RESET_ALL TEMPLATE_DIR = "templates/app" logger = get_logger("fastmsa.command") class FastMSACommand: def __init__(self): """Constructor. 작업 순서: 1. `<pkg_name>/config.py` 위치를 찾기 위해 앱 name 을 구한다. 2. <pkg_name> 은 암시적으로는 현재 경로의 이름인데, `setup.cfg` 파일에 `[fastmsa]` 섹션에도 지정 가능하다. 3. config 파일을 로드해서 나머지 정보를 읽는다. """ self.path = Path(os.path.abspath(".")) self.msa = cast(FastMSA, FastMSA.load_from_config(self.path))
import logging import re from contextlib import AbstractContextManager, contextmanager from typing import Any, Callable, Generator, Optional, Type, Union, cast from sqlalchemy import MetaData, create_engine from sqlalchemy.engine import Engine from sqlalchemy.orm import clear_mappers as _clear_mappers from sqlalchemy.orm import sessionmaker from sqlalchemy.orm.session import Session from sqlalchemy.pool import Pool, StaticPool from fastmsa.core import AbstractFastMSA from fastmsa.logging import get_logger logger = get_logger("fastmsa.orm") SessionMaker = Callable[[], Session] """Session 팩토리 타입.""" ScopedSession = AbstractContextManager[Session] metadata: Optional[MetaData] = None __session_factory: Optional[SessionMaker] = None def set_default_sessionmaker(sessionmaker: SessionMaker): global __session_factory __session_factory = sessionmaker def init_default_sessionmaker(
from dataclasses import asdict from fastmsa.core import AbstractPubsubClient from fastmsa.event import on_command, on_event from fastmsa.logging import get_logger from fastmsa.uow import AbstractUnitOfWork, SqlAlchemyUnitOfWork from tests.app.adapters import email from tests.app.domain import commands, events from tests.app.domain.aggregates import Product from tests.app.domain.models import Batch, OrderLine logger = get_logger("fastmsa.handlers.batch") class InvalidSku(Exception): """배치의 SKU와 다른 SKU를 할당하려 할 때 발생하는 예외입니다.""" ... @on_event(events.OutOfStock) def send_out_of_stock_notification(event: events.OutOfStock): """OutOfStock 예외 이벤트 발생시 에러 이메일을 발송합니다.""" email.send( "[email protected]\n", f"Out of stock for {event.sku}", ) @on_command(commands.CreateBatch) def add_batch(e: commands.CreateBatch, uow: AbstractUnitOfWork):
from dataclasses import asdict, dataclass, is_dataclass from typing import Callable, Optional, Any import aioredis # type: ignore from fastmsa.core import ( AbstractChannelListener, AbstractFastMSA, AbstractMessageBroker, AbstractPubsubClient, ) from fastmsa.event import messagebroker from fastmsa.logging import get_logger from fastmsa.utils import Fore, bold logger = get_logger("fastmsa.redis") @dataclass class RedisConnectInfo: host: str port: int @property def conn_args(self): return {"host": self.host, "port": self.port} @property def url(self) -> str: return f"redis://{self.host}:{self.port}"
from __future__ import annotations from typing import Optional from fastmsa.core import Aggregate from fastmsa.logging import get_logger from tests.app.domain import commands, events from tests.app.domain.models import Batch, OrderLine logger = get_logger("tests.app.domain") class Product(Aggregate[Batch]): def __init__(self, sku: str, items: list[Batch], version_number: int = 0): self.id = sku # Entity 프로토콜을 준수하기 위해 반드시 정의해야 하는 속성. self.sku = sku self.items = items self.version_number = version_number def allocate(self, line: OrderLine) -> Optional[str]: try: if not self.items: raise StopIteration batch = next(b for b in sorted(self.items) if b.can_allocate(line)) batch.allocate(line) self.version_number += 1 self.messages.append( events.Allocated( orderid=line.orderid, sku=line.sku, qty=line.qty,