• Stars
    star
    118
  • Rank 298,176 (Top 6 %)
  • Language
    Python
  • License
    Apache License 2.0
  • Created over 4 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

django-cqrs is an Django application, that implements CQRS data synchronization between several Django micro-services

Django CQRS

pyversions PyPI Docs Coverage GitHub Workflow Status PyPI status Quality Gate Status PyPI Downloads GitHub

django-cqrs is an Django application, that implements CQRS data synchronisation between several Django microservices.

CQRS

In Connect we have a rather complex Domain Model. There are many microservices, that are decomposed by subdomain and which follow database-per-service 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 is totally enough. But, some services are working too slowly with API JOINS, so another pattern needs to be applied.

The pattern, that solves this issue is called CQRS - Command Query Responsibility Segregation. 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 published by the service that owns the data. Data is eventually consistent and that's okay for non-critical business transactions.

Documentation

Full documentation is available at https://django-cqrs.readthedocs.org.

Examples

You can find an example project here

Integration

  • Setup RabbitMQ
  • Install django-cqrs
  • Apply changes to master service, according to RabbitMQ settings
# models.py

from django.db import models
from dj_cqrs.mixins import MasterMixin, RawMasterMixin


class Account(MasterMixin, models.Model):
    CQRS_ID = 'account'
    CQRS_PRODUCE = True  # set this to False to prevent sending instances to Transport
    
    
class Author(MasterMixin, models.Model):
    CQRS_ID = 'author'
    CQRS_SERIALIZER = 'app.api.AuthorSerializer'


# For cases of Diamond Multi-inheritance or in case of Proxy Django-models the following approach could be used:
from mptt.models import MPTTModel
from dj_cqrs.metas import MasterMeta

class ComplexInheritanceModel(MPTTModel, RawMasterMixin):
    CQRS_ID = 'diamond'

class BaseModel(RawMasterMixin):
    CQRS_ID = 'base'

class ProxyModel(BaseModel):
    class Meta:
        proxy = True

MasterMeta.register(ComplexInheritanceModel)
MasterMeta.register(BaseModel)
# settings.py

CQRS = {
    'transport': 'dj_cqrs.transport.rabbit_mq.RabbitMQTransport',
    'host': RABBITMQ_HOST,
    'port': RABBITMQ_PORT,
    'user': RABBITMQ_USERNAME,
    'password': RABBITMQ_PASSWORD,
}
  • Apply changes to replica service, according to RabbitMQ settings
from django.db import models
from dj_cqrs.mixins import ReplicaMixin


class AccountRef(ReplicaMixin, models.Model):
    CQRS_ID = 'account'
    
    id = models.IntegerField(primary_key=True)
    

class AuthorRef(ReplicaMixin, models.Model):
    CQRS_ID = 'author'
    CQRS_CUSTOM_SERIALIZATION = True
    
    @classmethod
    def cqrs_create(cls, sync, mapped_data, previous_data=None, meta=None):
        # Override here
        pass
        
    def cqrs_update(self, sync, mapped_data, previous_data=None, meta=None):
        # Override here
        pass
# settings.py

CQRS = {
    'transport': 'dj_cqrs.transport.RabbitMQTransport',
    'queue': 'account_replica',
    'host': RABBITMQ_HOST,
    'port': RABBITMQ_PORT,
    'user': RABBITMQ_USERNAME,
    'password': RABBITMQ_PASSWORD,
}
  • Apply migrations on both services
  • Run consumer worker on replica service. Management command: python manage.py cqrs_consume -w 2

Notes

  • 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.
  • Please, avoid saving different instances of the same entity within transaction to reduce syncing and potential racing on replica side.
  • Updating of related model won't trigger CQRS automatic synchronization for master model. This needs to be done manually.
  • By default update_fields doesn't trigger CQRS logic, but it can be overridden for the whole application in settings:
settings.CQRS = {
    ...
    'master': {
        'CQRS_AUTO_UPDATE_FIELDS': True,
    },
    ...
}

or a special flag can be used in each place, where it's required to trigger CQRS flow:

instance.save(update_fields=['name'], update_cqrs_fields=True)
  • When only needed instances need to be synchronized, there is a method is_sync_instance to set filtering rule. It's important to understand, that CQRS counting works even without syncing and rule is applied every time model is updated.

Example:

class FilteredSimplestModel(MasterMixin, models.Model):
    CQRS_ID = 'filter'

    name = models.CharField(max_length=200)

    def is_sync_instance(self):
        return len(str(self.name)) > 2

Django Admin

Add action to synchronize master items from Django Admin page.

from django.db import models
from django.contrib import admin

from dj_cqrs.admin_mixins import CQRSAdminMasterSyncMixin


class AccountAdmin(CQRSAdminMasterSyncMixin, admin.ModelAdmin):
    ...


admin.site.register(models.Account, AccountAdmin)
  • If necessary, override _cqrs_sync_queryset from CQRSAdminMasterSyncMixin to adjust the QuerySet and use it for synchronization.

Utilities

Bulk synchronizer without transport (usage example: it may be used for initial configuration). May be used at planned downtime.

  • On master service: python manage.py cqrs_bulk_dump --cqrs-id=author -> author.dump
  • On replica service: python manage.py cqrs_bulk_load -i=author.dump

Filter synchronizer over transport (usage example: sync some specific records to a given replica). Can be used dynamically.

  • To sync all replicas: python manage.py cqrs_sync --cqrs-id=author -f={"id__in": [1, 2]}
  • To sync all instances only with one replica: python manage.py cqrs_sync --cqrs-id=author -f={} -q=replica

Set of diff synchronization tools:

  • To get diff and synchronize master service with replica service in K8S:
kubectl exec -i MASTER_CONTAINER -- python manage.py cqrs_diff_master --cqrs-id=author | 
    kubectl exec -i REPLICA_CONTAINER -- python manage.py cqrs_diff_replica |
    kubectl exec -i MASTER_CONTAINER -- python manage.py cqrs_diff_sync
  • If it's important to check sync and clean up deleted objects within replica service in K8S:
kubectl exec -i REPLICA_CONTAINER -- python manage.py cqrs_deleted_diff_replica --cqrs-id=author | 
    kubectl exec -i MASTER_CONTAINER -- python manage.py cqrs_deleted_diff_master |
    kubectl exec -i REPLICA_CONTAINER -- python manage.py cqrs_deleted_sync_replica

Development

  1. Python >= 3.8
  2. Install dependencies requirements/dev.txt
  3. We use isort library to order and format our imports, and black - to format the code. We check it using flake8-isort and flake8-black libraries (automatically on flake8 run).
    For convenience you may run isort . && black . to format the code.

Testing

Unit testing

  1. Python >= 3.8
  2. Install dependencies requirements/test.txt
  3. export PYTHONPATH=/your/path/to/django-cqrs/

Run tests with various RDBMS:

  • cd integration_tests
  • DB=postgres docker-compose -f docker-compose.yml -f rdbms.yml run app_test
  • DB=mysql docker-compose -f docker-compose.yml -f rdbms.yml run app_test

Check code style: flake8 Run tests: pytest

Tests reports are generated in tests/reports.

  • out.xml - JUnit test results
  • coverage.xml - Coverage xml results

To generate HTML coverage reports use: --cov-report html:tests/reports/cov_html

Integrational testing

  1. docker-compose
  2. cd integration_tests
  3. docker-compose run master

More Repositories

1

django-rql

Django RQL library
Python
93
star
2

lib-rql

Python RQL filters
Python
22
star
3

javascript-rql

Javascript RQL lib to transform js object to rql query string
JavaScript
22
star
4

connect-python-sdk

Python SDK for the CloudBlue Connect Platform.
Python
19
star
5

connect-php-sdk

PHP SDK for the CloudBlue Connect Platform.
PHP
16
star
6

connect-python-openapi-client

Connect API Client based on the OpenAPI specification for Python 3.7+
Python
15
star
7

connect-cli

Command-line utility for CloudBlue Connect.
Python
15
star
8

apsconnect-cli

Warning: EOL for January 2021. CloudBlue Connect CLI tool to automate APS Package management in the CloudBlue Commerce instance (hub).
Python
14
star
9

connect-javascript-sdk

JavaScript (Node.js) SDK for the CloudBlue Connect Platform.
JavaScript
12
star
10

connect-fulfillment-zapier-app

Zapier APP for the CloudBlue Connect Platform.
JavaScript
11
star
11

connect-extension-runner

Python
10
star
12

connect-ui-toolkit

UI Toolkit to build between others extensions for CloudBlue Connect
JavaScript
9
star
13

connect-haxe-sdk

Haxe Connect SDK for the CloudBlue Connect Platform.
Haxe
9
star
14

django-postgresql-reconnect

PostgreSQL database reconnection helper
Python
9
star
15

osaAPI

A python client for the CloudBlue Commerce APIs (ex. Odin Automation)
Python
8
star
16

django-mongoengine-rql

Our implementation of RQL when using a Mongo Engine with Django
Python
8
star
17

material-svg

Material Design Icons from Google plus the ones created by CloudBlue Connect
JavaScript
8
star
18

connect-reports

Out of the box reports for CloudBlue Connect
Python
7
star
19

connect-eaas-core

Core repository for Conned Devops Extensions
Python
6
star
20

xml-doclet

A doclet to output javadoc as an XML document.
Java
6
star
21

connect-reports-core

Python
6
star
22

connect-quickstart-template-for-php

Sample processor script for the CloudBlue Connect Platform PHP SDK.
PHP
6
star
23

connect-playground

Connect Playground - easy way to fill in your account with production-like objects
Python
5
star
24

telegram-django

Python
5
star
25

connect-jsdoc-theme

Repository for JSDOC CloudBlue theme, used by the CloudBlue Connect SDK Documentation.
JavaScript
5
star
26

connect-workflows

Structured repository of Connect modules, objects and their statuses
HTML
5
star
27

connect-extension-telegram-notifications

Connect Extension for Telegram Notifications
Python
5
star
28

connect-php-sdk-migration-framework

Migration framework for the CloudBlue Connect PHP SDK for the migration scenarios automation
PHP
5
star
29

connect-report-python-boilerplate

Template to quickly bootstrap custom reports for CloudBlue Connect
Python
4
star
30

loudhailer

Broadcast library for asynchronous applications (mainly websockets but not only)
Python
4
star
31

country-flag-mapper

JavaScript
4
star
32

connect-extension-service-delivery-notifications

Connect Extension to send email notifications to buyers
JavaScript
4
star
33

connect-markdown-renderer

Python
4
star
34

aps1toconnect

Sample tool to perform APS 1.2 to Connect migration for CB Commerce
Python
4
star
35

processor-acronis-customer-centric

CloudBlue Connect Processor for Acronis
Python
3
star
36

homebrew-connect

Base repository for connect-cli and macOS Homebrew formulae
Ruby
3
star
37

connect-extension-xvs

Connect extension for XVS Commerce system integration
Python
3
star
38

connect-python-sdk-migration-framework

Migration framework for the CloudBlue Connect Platform Python SDK for the migration scenarios automation.
Python
3
star
39

peewee-model-utils

Python
2
star
40

connect-devops-testing-library

Testing Library useful for testing devops extensions
Python
2
star
41

connect-reports-runner

Python
2
star
42

connect-extension-mulesoft

CloudBlue Connect Extension for MuleSoft
Java
2
star
43

connect-java-sdk

CloudBlue Connect Platform Java SDK Created from the Haxe code.
Java
2
star
44

connect-intern-program-python-task

Cloudblue Connect tasks for Intern Program
2
star
45

eaas-e2e-mock

Sample extension for running e2e for Connect Devops Module
Python
2
star
46

microsoft_reports

Repository that contains Connect custom reports for microsoft products
Python
2
star
47

connect-extension-for-cb-commerce

Connext Extension for CloudBlue Commerce
Python
1
star
48

connect-extension-customer-portal

Python
1
star
49

eaas-e2e-transformations-mock

Transformations Mock for E2E runs
JavaScript
1
star
50

cloudblue-connect-google-products-reports

Python
1
star
51

connect-transformations

Standard library of Connect transformations
JavaScript
1
star
52

eaas-e2e-ma-mock

EaaS Test Extension for E2E on MultiAccount extensions
JavaScript
1
star
53

processor-malwarebytes

Malwarebytes Connect Processor
Python
1
star
54

connect-extension-boomi

This repository holds the source code of CloudBlue Connect Extension for Dell Boomi
Java
1
star
55

adobe_reports

Reports for Adobe product
Python
1
star
56

connect-processor-template-for-python

Python
1
star
57

connect-extension-dbaas

DBaaS Extension for CloudBlue Connect
Python
1
star
58

google-workspace-connect-report

Python
1
star
59

extension-xv-datalake

Extension for Xvantage DL integration
JavaScript
1
star
60

eaas-e2e-asyncmock

Repository containing a mock using Async client
Python
1
star