{"id":48489622,"url":"https://github.com/cloudblue/django-cqrs","last_synced_at":"2026-04-07T11:04:35.417Z","repository":{"id":37418287,"uuid":"267261793","full_name":"cloudblue/django-cqrs","owner":"cloudblue","description":"django-cqrs is an Django application, that implements CQRS data synchronization between several Django micro-services","archived":false,"fork":false,"pushed_at":"2026-03-25T16:10:51.000Z","size":1061,"stargazers_count":128,"open_issues_count":8,"forks_count":26,"subscribers_count":6,"default_branch":"master","last_synced_at":"2026-03-26T18:04:36.097Z","etag":null,"topics":["cqrs","django","django-cqrs","highload","microservices","python","rabbitmq"],"latest_commit_sha":null,"homepage":"https://django-cqrs.readthedocs.io/en/latest/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudblue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-05-27T08:19:10.000Z","updated_at":"2026-03-25T16:12:31.000Z","dependencies_parsed_at":"2024-06-21T02:14:39.010Z","dependency_job_id":"6817583e-5e3c-4e82-9dc2-d98c23c7cc8d","html_url":"https://github.com/cloudblue/django-cqrs","commit_stats":null,"previous_names":[],"tags_count":75,"template":false,"template_full_name":null,"purl":"pkg:github/cloudblue/django-cqrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-cqrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-cqrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-cqrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-cqrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudblue","download_url":"https://codeload.github.com/cloudblue/django-cqrs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-cqrs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31509946,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cqrs","django","django-cqrs","highload","microservices","python","rabbitmq"],"created_at":"2026-04-07T11:04:32.632Z","updated_at":"2026-04-07T11:04:35.405Z","avatar_url":"https://github.com/cloudblue.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Django CQRS\n===========\n![pyversions](https://img.shields.io/pypi/pyversions/django-cqrs.svg)\n![PyPI](https://img.shields.io/pypi/v/django-cqrs)\n[![Docs](https://readthedocs.org/projects/django-cqrs/badge/?version=latest)](https://readthedocs.org/projects/django-cqrs)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=django-cqrs\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=django-cqrs)\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/cloudblue/django-cqrs/build.yml)\n[![PyPI status](https://img.shields.io/pypi/status/django-cqrs.svg)](https://pypi.python.org/pypi/django-cqrs/)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=django-cqrs\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=django-cqrs)\n[![PyPI Downloads](https://img.shields.io/pypi/dm/django-cqrs)](https://pypi.org/project/django-cqrs/)\n![GitHub](https://img.shields.io/github/license/cloudblue/django-cqrs)\n\n`django-cqrs` is an Django application, that implements CQRS data synchronisation between several Django microservices.\n\n\nCQRS\n----\nIn Connect we have a rather complex Domain Model. There are many microservices, that are [decomposed by subdomain](https://microservices.io/patterns/decomposition/decompose-by-subdomain.html) and which follow [database-per-service](https://microservices.io/patterns/data/database-per-service.html) pattern. These microservices have rich and consistent APIs. They are deployed in cloud k8s cluster and scale automatically under load. Many of these services aggregate data from other ones and usually [API Composition](https://microservices.io/patterns/data/api-composition.html) is totally enough. But, some services are working too slowly with API JOINS, so another pattern needs to be applied.\n\nThe pattern, that solves this issue is called [CQRS - Command Query Responsibility Segregation](https://microservices.io/patterns/data/cqrs.html). Core idea behind this pattern is that view databases (replicas) are defined for efficient querying and DB joins. Applications keep their replicas up to data by subscribing to [Domain events](https://microservices.io/patterns/data/domain-event.html) published by the service that owns the data. Data is [eventually consistent](https://en.wikipedia.org/wiki/Eventual_consistency) and that's okay for non-critical business transactions.\n\n\nDocumentation\n=============\n\nFull documentation is available at [https://django-cqrs.readthedocs.org](https://django-cqrs.readthedocs.org).\n\n\nExamples\n========\n\nYou can find an example project [here](examples/demo_project/README.md)\n\nIntegration\n-----------\n* Setup `RabbitMQ`\n* Install `django-cqrs`\n* Apply changes to master service, according to RabbitMQ settings\n```python\n# models.py\n\nfrom django.db import models\nfrom dj_cqrs.mixins import MasterMixin, RawMasterMixin\n\n\nclass Account(MasterMixin, models.Model):\n    CQRS_ID = 'account'\n    CQRS_PRODUCE = True  # set this to False to prevent sending instances to Transport\n    \n    \nclass Author(MasterMixin, models.Model):\n    CQRS_ID = 'author'\n    CQRS_SERIALIZER = 'app.api.AuthorSerializer'\n\n\n# For cases of Diamond Multi-inheritance or in case of Proxy Django-models the following approach could be used:\nfrom mptt.models import MPTTModel\nfrom dj_cqrs.metas import MasterMeta\n\nclass ComplexInheritanceModel(MPTTModel, RawMasterMixin):\n    CQRS_ID = 'diamond'\n\nclass BaseModel(RawMasterMixin):\n    CQRS_ID = 'base'\n\nclass ProxyModel(BaseModel):\n    class Meta:\n        proxy = True\n\nMasterMeta.register(ComplexInheritanceModel)\nMasterMeta.register(BaseModel)\n```\n\n```python\n# settings.py\n\nCQRS = {\n    'transport': 'dj_cqrs.transport.rabbit_mq.RabbitMQTransport',\n    'host': RABBITMQ_HOST,\n    'port': RABBITMQ_PORT,\n    'user': RABBITMQ_USERNAME,\n    'password': RABBITMQ_PASSWORD,\n}\n\n```\n* Apply changes to replica service, according to RabbitMQ settings\n```python\nfrom django.db import models\nfrom dj_cqrs.mixins import ReplicaMixin\n\n\nclass AccountRef(ReplicaMixin, models.Model):\n    CQRS_ID = 'account'\n    \n    id = models.IntegerField(primary_key=True)\n    \n\nclass AuthorRef(ReplicaMixin, models.Model):\n    CQRS_ID = 'author'\n    CQRS_CUSTOM_SERIALIZATION = True\n    \n    @classmethod\n    def cqrs_create(cls, sync, mapped_data, previous_data=None, meta=None):\n        # Override here\n        pass\n        \n    def cqrs_update(self, sync, mapped_data, previous_data=None, meta=None):\n        # Override here\n        pass\n```\n\n```python\n# settings.py\n\nCQRS = {\n    'transport': 'dj_cqrs.transport.RabbitMQTransport',\n    'queue': 'account_replica',\n    'host': RABBITMQ_HOST,\n    'port': RABBITMQ_PORT,\n    'user': RABBITMQ_USERNAME,\n    'password': RABBITMQ_PASSWORD,\n}\n```\n* Apply migrations on both services\n* Run consumer worker on replica service. Management command: `python manage.py cqrs_consume -w 2`\n\nNotes\n-----\n\n* When there are master models with related entities in CQRS_SERIALIZER, it's important to have operations within atomic transactions. CQRS sync will happen on transaction commit. \n* Please, avoid saving different instances of the same entity within transaction to reduce syncing and potential racing on replica side.\n* Updating of related model won't trigger CQRS automatic synchronization for master model. This needs to be done manually.\n* By default `update_fields` doesn't trigger CQRS logic, but it can be overridden for the whole application in settings:\n```python\nsettings.CQRS = {\n    ...\n    'master': {\n        'CQRS_AUTO_UPDATE_FIELDS': True,\n    },\n    ...\n}\n```\nor a special flag can be used in each place, where it's required to trigger CQRS flow:\n```python\ninstance.save(update_fields=['name'], update_cqrs_fields=True)\n```\n* When only needed instances need to be synchronized, there is a method `is_sync_instance` to set filtering rule. \nIt's important to understand, that CQRS counting works even without syncing and rule is applied every time model is updated.\n\nExample:\n```python\n\nclass FilteredSimplestModel(MasterMixin, models.Model):\n    CQRS_ID = 'filter'\n\n    name = models.CharField(max_length=200)\n\n    def is_sync_instance(self):\n        return len(str(self.name)) \u003e 2\n```\n\nDjango Admin\n-----------\n\nAdd action to synchronize master items from Django Admin page.\n\n```python\nfrom django.db import models\nfrom django.contrib import admin\n\nfrom dj_cqrs.admin_mixins import CQRSAdminMasterSyncMixin\n\n\nclass AccountAdmin(CQRSAdminMasterSyncMixin, admin.ModelAdmin):\n    ...\n\n\nadmin.site.register(models.Account, AccountAdmin)\n\n```\n\n* If necessary, override ```_cqrs_sync_queryset``` from ```CQRSAdminMasterSyncMixin``` to adjust the QuerySet and use it for synchronization.\n\n\nUtilities\n---------\nBulk synchronizer without transport (usage example: it may be used for initial configuration). May be used at planned downtime.\n* On master service: `python manage.py cqrs_bulk_dump --cqrs-id=author` -\u003e `author.dump`\n* On replica service: `python manage.py cqrs_bulk_load -i=author.dump`\n\nFilter synchronizer over transport (usage example: sync some specific records to a given replica). Can be used dynamically.\n* To sync all replicas: `python manage.py cqrs_sync --cqrs-id=author -f={\"id__in\": [1, 2]}`\n* To sync all instances only with one replica: `python manage.py cqrs_sync --cqrs-id=author -f={} -q=replica`\n\nSet of diff synchronization tools:\n* To get diff and synchronize master service with replica service in K8S: \n```bash\nkubectl exec -i MASTER_CONTAINER -- python manage.py cqrs_diff_master --cqrs-id=author | \n    kubectl exec -i REPLICA_CONTAINER -- python manage.py cqrs_diff_replica |\n    kubectl exec -i MASTER_CONTAINER -- python manage.py cqrs_diff_sync\n```\n\n* If it's important to check sync and clean up deleted objects within replica service in K8S:\n```bash\nkubectl exec -i REPLICA_CONTAINER -- python manage.py cqrs_deleted_diff_replica --cqrs-id=author | \n    kubectl exec -i MASTER_CONTAINER -- python manage.py cqrs_deleted_diff_master |\n    kubectl exec -i REPLICA_CONTAINER -- python manage.py cqrs_deleted_sync_replica\n```\n\nDevelopment\n===========\n\n1. Python \u003e= 3.10\n2. Install dependencies `requirements/dev.txt`\n3. We use `isort` library to order and format our imports, and `black` - to format the code. \nWe check it using `flake8-isort` and `flake8-black` libraries (automatically on `flake8` run).  \nFor convenience you may run `isort . \u0026\u0026 black .` to format the code.\n\n\nTesting\n=======\n\nUnit testing\n------\n1. Python \u003e= 3.10\n2. Install dependencies `requirements/test.txt`\n3. `export PYTHONPATH=/your/path/to/django-cqrs/`\n\nRun tests with various RDBMS:\n- `cd integration_tests`\n- `DB=postgres docker compose -f docker-compose.yml -f rdbms.yml run app_test`\n- `DB=mysql docker compose -f docker-compose.yml -f rdbms.yml run app_test`\n\nCheck code style: `flake8`\nRun tests: `pytest`\n\nTests reports are generated in `tests/reports`. \n* `out.xml` - JUnit test results\n* `coverage.xml` - Coverage xml results\n\nTo generate HTML coverage reports use:\n`--cov-report html:tests/reports/cov_html`\n\n\nIntegrational testing\n------\n1. docker compose\n2. `cd integration_tests`\n3. `docker compose run master`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudblue%2Fdjango-cqrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudblue%2Fdjango-cqrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudblue%2Fdjango-cqrs/lists"}