Factorizer/Cluster ERP WikiCluster ERP English
Ana sayfa / Katkı

Öğretici

Bir arka uç modülü yazın

Boş bir klasörden modelleri, rotaları, metadata'sı ve EventBus işleyicileri olan, kayıtlı bir Cluster ERP arka uç modülüne rehberli yürüyüş.

Bu eğitim bir geliştiriciyi src/cluster_erp/modules/ altındaki boş bir klasörden; sunucunun yüklediği, rota sunan, istemcilere metadata yayımlayan ve diğer modüllerle EventBus üzerinden konuşan bir modüle götürür. Çalışılmış örnek olarak küçük records modülünü kullanır. Web ekranlarını ayrıntılı ele almaz; onlar için metadata ile ekran ekleyin sayfasına bakın.

Ne kuracaksınız

Sonunda, Module sınıfı ModuleBase'den türeyen bir module.py içeren src/cluster_erp/modules/<module_name>/ paketine sahip olursunuz. Sunucu onu açılışta keşfeder, yaşam döngüsü yöntemlerini çağırır, rotalarını /api/v1/<module_name> altına bağlar ve metadata'sını /api/v1/_meta içine katar. Mimari testler geçer. Sözleşmenin arka planı modül yaşam döngüsü sayfasındadır.

Sıra şeması sunucunun bir modüle yaptığı çağrıları gösterir.

sequenceDiagram
 participant App as server/app.py lifespan
 participant Reg as ModuleRegistry
 participant Mod as Module
 App->>Reg: load_all(app, bus, database, enabled_modules)
 Reg->>Mod: initialize(database)
 Reg->>Mod: register_routes(app)
 Reg->>Mod: register_events(bus)
 App->>Mod: /api/v1/_meta için get_metadata()
 App->>Reg: shutdown_all()
 Reg->>Mod: shutdown()

Düzyazıyla: açılışta lifespan, etkin modüllerin listesiyle load_all çağırır (src/cluster_erp/server/app.py:140). Kayıt defteri her ad için cluster_erp.modules.<name>.module modülünü import eder, Module() oluşturur ve initialize, register_routes ve register_events yöntemlerini bu sırayla çağırır (src/cluster_erp/core/module_registry.py:39-52). Metadata rotası yüklenen her modülün get_metadata sonucunu effective_module_meta üzerinden okur (src/cluster_erp/server/app.py:1537-1538). Kapanışta kayıt defteri her modülde shutdown çağırır ve bir hatayı fırlatmaz, günlüğe yazar (src/cluster_erp/core/module_registry.py:74-80).

Başlamadan önce

  • Yerel geliştirme kurulumu sayfasındaki gibi çalışan bir çalışma kopyası ve veritabanı.
  • Python 3.12 veya üstü (pyproject.toml:9).
  • dev ekindeki geliştirme araçları: mypy, pytest ve Ruff (pyproject.toml:79-84).

Adım 1: Paketi oluşturun

Bir __init__.py ile src/cluster_erp/modules/<module_name>/ klasörünü oluşturun. Kayıt defteri cluster_erp.modules altındaki her paketi modül adayı sayar (src/cluster_erp/core/module_registry.py:26-30). Klasör adı, sınır testinin denetlediği modül adı olur (tests/test_architecture_boundaries.py:25-27).

Her alt modül için models.py, schemas.py, services.py ve routes.py içeren bir alt klasör tutun; src/cluster_erp/modules/records/deactivation_reasons/ buna örnektir. Her alt modül modelini import eden üst düzey bir models.py ekleyin. Alembic her modül için tam olarak bu dosyayı import eder (migrations/env.py:76-85); src/cluster_erp/modules/records/models.py bir örnektir.

Adım 2: Modelleri tanımlayın

cluster_erp.core.models içindeki BaseModel'den türetin. Böylece bir ULID birincil anahtar, kiracı sütunu, denetim sütunları, yumuşak silme sütunları ve version sayacı gelir (src/cluster_erp/core/models.py:17-78). Benzersizliği, DeactivationReason gibi, yalnız canlı satırlarda kısmi bir indeksle bildirin (src/cluster_erp/modules/records/deactivation_reasons/models.py:16-25). Para sütunları Numeric ve Decimal kullanır; bkz. para ve Decimal.

Adım 3: Göçü ekleyin

İş şeması yalnız Alembic geçmişinden gelir; bir modül tabloları asla kendisi oluşturmaz. migrations/versions/ altına bir revizyon ekleyin. Yöntem, adlandırma ve denetimler veritabanı göçleri sayfasındadır. CI tüm geçmişi boş bir veritabanına uygular ve tam olarak bir head ister (.github/workflows/ci.yml:113-117).

Adım 4: Şemaları, servisleri ve rotaları yazın

İş mantığını, örneğin DeactivationReasonService gibi, AsyncSession alan bir servis sınıfına koyun. Rotalar servisi oluşturur ve girdiyi Pydantic şemalarıyla doğrular. Her rotayı, sınır testinin başka bir modülden izin verdiği tek import olan require_permission ile koruyun (src/cluster_erp/modules/records/deactivation_reasons/routes.py:10, src/cluster_erp/modules/records/deactivation_reasons/routes.py:24-36, tests/test_architecture_boundaries.py:24).

<entity>.create, .read, .update ve .delete biçimindeki yetki kodları, modül metadata'sındaki her varlık için açılışta tohumlanır (src/cluster_erp/server/app.py:642-652, src/cluster_erp/modules/auth/permissions/services.py:44-54). Ayrıntılar roller ve izinler sayfasındadır.

Adım 5: module.py dosyasını yazın

Tam olarak Module adlı bir sınıfla module.py oluşturun. Bu sınıf yoksa kayıt defteri ModuleLoadError fırlatır (src/cluster_erp/core/module_registry.py:45-47). Soyut üyeleri uygulayın: name, version, description, initialize, register_routes, register_events, shutdown ve get_metadata (src/cluster_erp/core/module_base.py:24-74).

src/cluster_erp/modules/records/module.py:36-55 dosyasından:

    async def initialize(self, database: Database) -> None:
        import cluster_erp.modules.records.models  # noqa: F401

    def register_routes(self, app: FastAPI) -> None:
        from cluster_erp.modules.records.deactivation_reasons.routes import (
            router as reasons_router,
        )
        from cluster_erp.modules.records.lifecycle_routes import (
            router as lifecycle_router,
        )
        from cluster_erp.modules.records.retention.routes import (
            router as retention_router,
        )

        app.include_router(reasons_router, prefix=self.api_prefix)
        app.include_router(lifecycle_router, prefix=self.api_prefix)
        app.include_router(retention_router, prefix=self.api_prefix)

    def register_events(self, bus: EventBus) -> None:
        pass

api_prefix varsayılan olarak /api/v1/<name> olur (src/cluster_erp/core/module_base.py:36-38). Mimari test modülde en az bir rota dekoratörü ister (tests/test_architecture_boundaries.py:63-72).

Adım 6: Metadata'yı bildirin

get_metadata, modülün adını, simgesini, kenar çubuğunu ve varlıklarını taşıyan bir ModuleMeta döndürür (src/cluster_erp/core/metadata.py:719-740). records modülü bir varlık ve boş bir kenar çubuğu döndürür (src/cluster_erp/modules/records/module.py:60-76). Web uygulaması ve masaüstü istemci listelerini, formlarını ve kartlarını bu nesneden kurar; bkz. metadata modeli. tests/test_metadata_driven_rules.py keşfedilen her modülde get_metadata çağırır; bu yüzden yöntem veritabanı olmadan çalışmalıdır (tests/test_metadata_driven_rules.py:23-29).

Adım 7: Diğer modüllere bağlanın

Başka bir modülü import etmeyin. Bir şeye tepki vermek için register_events içinde bir Event'e abone olun; veri istemek için tam olarak bir modülün yanıtladığı bir Query gönderin (src/cluster_erp/core/event_bus.py:55-102). Yeni sözleşmeler dondurulmuş dataclass olarak src/cluster_erp/core/contracts.py dosyasına girer. products_and_services modülü iki kalıbı da gösterir: sorgu işleyicileri kaydeder ve bir olaya abone olur (src/cluster_erp/modules/products_and_services/module.py:131-147). Bir işleyicinin içinde, geçerli kiracının veritabanını okumak için request_database() kullanın (src/cluster_erp/core/module_base.py:40-59).

Adım 8: Modülü etkinleştirin

Lifespan yalnız enabled_modules içindeki adları yükler (src/cluster_erp/server/app.py:140). Varsayılan liste 28 ad içerir (src/cluster_erp/config/settings.py:539-568). Yeni adı oraya ekleyin veya listeyi ayarlar başvurusu sayfasında anlatıldığı gibi ortam üzerinden verin. Listede olup paketi olmayan bir ad uyarıyla atlanır (src/cluster_erp/core/module_registry.py:68-71).

Sonucunuzu denetleyin

CI'ın çalıştırdığı kapıları depo kökünden çalıştırın:

python -m ruff check src tests
python -m mypy src
python -m pytest -q --no-header -p no:cacheprovider

Kaynak: .github/workflows/ci.yml:51-52, .github/workflows/ci.yml:124. tests/test_architecture_boundaries.py:46-72 üzerinden çıkarılan beklenen sonuç: modül hiçbir ihlal listesinde görünmez ve module.py ya da API eksikliğiyle bildirilmez. Açılışta sunucu modülü "Loaded modules" satırında günlüğe yazar (src/cluster_erp/server/app.py:141-144).

Ne öğrendiniz

Sonraki adımlar

İlgili sayfalar


92f8051 numaralı commit ile 2026-11-13 tarihinde doğrulandı.

Doğrulanan commit 92f8051 · 2026-11-13