Skip to content

Architecture

The Netforce framework uses a model-driven architecture that separates business logic, data definition, and user interface concerns. This enables rapid development of ERP applications with minimal code duplication.

System Overview

┌─────────────────────────────────────────────────────────┐
│                    Client Layer                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    │
│  │   Browser   │  │ Mobile App  │  │ API Client  │    │
│  └─────────────┘  └─────────────┘  └─────────────┘    │
└─────────────────────────────────────────────────────────┘
                              │ HTTP/JSON-RPC
┌─────────────────────────────────────────────────────────┐
│                 Frontend Layer                          │
│  ┌───────────────────────────────────────────────────┐ │
│  │              React Application                    │ │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│  │  │  Form   │ │  List   │ │ Field   │ │ Widget  │ │ │
│  │  │Component│ │Component│ │Components│ │Components│ │ │
│  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│  └───────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────┐ │
│  │               RPC Client                          │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                              │ JSON-RPC over HTTP
┌─────────────────────────────────────────────────────────┐
│                 Backend Layer                           │
│  ┌───────────────────────────────────────────────────┐ │
│  │              JSON-RPC Controller                  │ │
│  │  (Authentication, Authorization, Request Routing) │ │
│  └───────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────┐ │
│  │                Model Layer                        │ │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│  │  │ Models  │ │ Fields  │ │ Methods │ │Function │ │ │
│  │  │         │ │         │ │         │ │Fields   │ │ │
│  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│  └───────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────┐ │
│  │               UI Definition Layer                 │ │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│  │  │ Layouts │ │ Actions │ │ Menus   │ │Dashboards│ │ │
│  │  │  (XML)  │ │  (XML)  │ │  (XML)  │ │  (XML)   │ │ │
│  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                              │ SQL
┌─────────────────────────────────────────────────────────┐
│                 Database Layer                          │
│  ┌───────────────────────────────────────────────────┐ │
│  │              PostgreSQL Database                  │ │
│  │    Tables, Indexes, Constraints, Triggers        │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

Core Principles

1. Model-Driven Development

Single Source of Truth: Models define both data structure and UI automatically.

# Define once in Python
class Invoice(Model):
    _name = "account.invoice"
    _fields = {
        "number": fields.Char("Invoice Number", required=True),
        "customer_id": fields.Many2One("contact", "Customer"),
        "lines": fields.One2Many("account.invoice.line", "invoice_id", "Lines"),
        "total_amount": fields.Decimal("Total", function="get_total")
    }
<!-- UI generated from model definition -->
<form model="account.invoice">
    <field name="number"/>
    <field name="customer_id"/>
    <field name="lines"/>
    <field name="total_amount" readonly="1"/>
</form>

2. Declarative UI Definition

XML-Based Layouts: UI structure defined declaratively, not imperatively.

<!-- Declare what you want, not how to build it -->
<list model="account.invoice">
    <field name="number" link="1"/>
    <field name="customer_id"/>  
    <field name="total_amount" show_total="1"/>
    <field name="state"/>
</list>

3. Generic Frontend Components

Reusable Components: One set of React components renders all models.

// Same Form component renders any model
<Form model="account.invoice" active_id={123}/>
<Form model="sale.order" active_id={456}/>
<Form model="hr.employee" active_id={789}/>

Module Architecture

Module Structure

netforce_module/
├── __init__.py                 # Module initialization
├── models/                     # Business logic layer
│   ├── __init__.py
│   ├── model_a.py             # Model definitions
│   └── model_b.py
├── layouts/                    # UI definition layer
│   ├── model_a_form.xml       # Form layouts
│   ├── model_a_list.xml       # List layouts
│   ├── model_b_board.xml      # Dashboard layouts
│   └── menu.xml               # Navigation menus
├── actions/                    # Action definitions
│   ├── model_a.xml            # List/form actions
│   └── reports.xml            # Report actions
├── templates/                  # Email/report templates
│   └── invoice_template.html
├── reports/                    # Report definitions
│   └── profit_loss.py
├── migrations/                 # Database migrations
│   └── 0001_initial.py
├── tests/                     # Unit tests
│   └── test_models.py
└── static/                    # Static assets
    └── css/
        └── custom.css

Module Loading

  1. Discovery: Modules discovered in nf_base/ directory
  2. Import: Python modules imported and models registered
  3. Layout Registration: XML layouts parsed and cached
  4. Action Registration: Actions linked to models and layouts
  5. Menu Building: Navigation structure assembled
  6. Database Sync: Models synchronized with database schema

Data Flow Architecture

Request/Response Cycle

┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│ Browser │    │Frontend │    │Backend  │    │Database │
│         │    │         │    │         │    │         │
└─────────┘    └─────────┘    └─────────┘    └─────────┘
      │              │              │              │
      │─────────────▶│              │              │
      │ User Action  │              │              │
      │              │──────────────▶              │
      │              │ JSON-RPC Call│              │
      │              │              │──────────────▶
      │              │              │ SQL Query    │
      │              │              │◀─────────────│
      │              │              │ Results      │
      │              │◀──────────────              │
      │              │ JSON Response│              │
      │◀─────────────│              │              │
      │ UI Update    │              │              │

CRUD Operations Flow

Create Operation

Frontend                Backend                 Database
   │                       │                      │
   │ POST {name: "John"}   │                      │
   ├──────────────────────▶│ validate_data()      │
   │                       ├──────────────────────▶│ INSERT
   │                       │◀──────────────────────│ {id: 123}
   │                       │ trigger_create()     │
   │                       │ send_notifications() │
   │ {id: 123, name: "John"}│                      │
   │◀──────────────────────│                      │

Read Operation

Frontend                Backend                 Database
   │                       │                      │
   │ GET /record/123       │                      │
   ├──────────────────────▶│ check_permissions()  │
   │                       ├──────────────────────▶│ SELECT
   │                       │◀──────────────────────│ {record}
   │                       │ compute_fields()     │
   │                       │ format_output()      │
   │ {formatted_record}    │                      │
   │◀──────────────────────│                      │

Security Architecture

Multi-Layer Security

┌─────────────────────────────────────────────┐
│            Frontend Security                │
│  • Input validation                         │
│  • XSS prevention                          │
│  • CSRF protection                         │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│           Transport Security               │
│  • HTTPS encryption                        │
│  • Token-based authentication             │
│  • Request/response validation            │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│           Backend Security                 │
│  • User authentication                     │
│  • Role-based permissions                 │
│  • Model-level access control            │
│  • Field-level security                  │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│           Database Security                │
│  • Connection encryption                   │
│  • SQL injection prevention               │
│  • Row-level security (RLS)               │
│  • Audit logging                          │
└─────────────────────────────────────────────┘

Permission System

# Model-level permissions
class Invoice(Model):
    _access_rules = [
        ["group_user", "read", True],
        ["group_manager", "write", True],
        ["group_admin", "delete", True]
    ]

    # Field-level security
    _fields = {
        "internal_notes": fields.Text("Notes", 
                                    access_rule="group_manager")
    }

    # Method-level security
    @access_required("group_manager")
    def approve(self, ids, context={}):
        # Only managers can approve
        pass

Scalability Architecture

Horizontal Scaling

┌─────────────────────────────────────────────┐
│              Load Balancer                  │
└─────────────────┬───────────────────────────┘
       ┌──────────┼──────────┐
       │          │          │
       ▼          ▼          ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Frontend │ │Frontend │ │Frontend │
│Server 1 │ │Server 2 │ │Server N │
└─────────┘ └─────────┘ └─────────┘
       │          │          │
       └──────────┼──────────┘
       ┌──────────┼──────────┐
       │          │          │
       ▼          ▼          ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Backend  │ │Backend  │ │Backend  │
│Server 1 │ │Server 2 │ │Server N │
└─────────┘ └─────────┘ └─────────┘
       │          │          │
       └──────────┼──────────┘
            ┌─────────┐
            │Database │
            │Cluster  │
            └─────────┘

Caching Strategy

# Multi-level caching
┌─────────────────┐
 Browser Cache     Static assets, API responses
├─────────────────┤
 CDN Cache         Global content distribution  
├─────────────────┤
 Frontend Cache    UI metadata, field definitions
├─────────────────┤
 Backend Cache     Model definitions, computed fields
├─────────────────┤
 Database Cache    Query results, connection pooling
└─────────────────┘

Development Workflow

1. Model-First Development

# Step 1: Define model
class Product(Model):
    _name = "product.product"
    _fields = {
        "name": fields.Char("Name", required=True),
        "price": fields.Decimal("Price"),
        "category_id": fields.Many2One("product.category", "Category")
    }
<!-- Step 2: Create layouts automatically generated or customized -->
<form model="product.product">
    <field name="name"/>
    <field name="price"/>
    <field name="category_id"/>
</form>
<!-- Step 3: Define actions -->
<action>
    <field name="view">list</field>
    <field name="model">product.product</field>
    <field name="string">Products</field>
</action>

2. Iterative Enhancement

# Enhance model with business logic
def calculate_tax(self, ids, context={}):
    for product in self.browse(ids):
        # Tax calculation logic
        pass

# Add computed fields
"price_with_tax": fields.Decimal("Price (Tax Incl)", 
                                function="get_price_with_tax")

3. UI Customization

<!-- Customize layouts for specific needs -->
<form model="product.product">
    <top>
        <button string="Update Prices" method="bulk_price_update"/>
    </top>
    <group string="Basic Information">
        <field name="name" span="8"/>
        <field name="active" span="4"/>
        <field name="category_id" span="6"/>
        <field name="price" span="6"/>
    </group>
</form>

Integration Architecture

External System Integration

# API Integration
class ExternalAPIIntegration(Model):
    def sync_with_external_system(self, ids, context={}):
        for record in self.browse(ids):
            # Call external API
            response = requests.post(external_api_url, {
                'data': record.serialize()
            })
            # Process response
            record.update_from_external(response.json())

# Webhook Integration  
@webhook_handler('/webhook/external-system')
def handle_external_webhook(self, data):
    # Process incoming webhook
    model = get_model('my.model')
    model.process_external_update(data)

Database Integration

# Multi-database support
class DataMigration(Model):
    def migrate_from_legacy(self, source_db_config):
        # Connect to legacy system
        legacy_conn = connect_to_legacy_db(source_db_config)

        # Extract data
        legacy_data = legacy_conn.query("SELECT * FROM old_table")

        # Transform and load
        for row in legacy_data:
            transformed = self.transform_legacy_record(row)
            self.create(transformed)

Performance Architecture

Query Optimization

# Efficient data loading
def load_with_related(self, ids, context={}):
    # Single query with joins instead of N+1 queries
    records = self.browse(ids, prefetch=[
        'customer_id',
        'lines.product_id',
        'lines.product_id.category_id'
    ])
    return records

# Batch processing
def process_batch(self, ids, context={}):
    # Process in chunks to avoid memory issues
    batch_size = 1000
    for i in range(0, len(ids), batch_size):
        batch_ids = ids[i:i+batch_size]
        self._process_chunk(batch_ids)

Memory Management

# Memory-efficient processing
def process_large_dataset(self, context={}):
    # Stream processing instead of loading all at once
    offset = 0
    limit = 1000

    while True:
        ids = self.search([], offset=offset, limit=limit)
        if not ids:
            break

        # Process chunk
        self.process_chunk(ids)

        # Clear cache to free memory
        self.invalidate_cache()
        offset += limit

Monitoring and Observability

Logging Architecture

# Structured logging
import logging
from netforce.logger import get_logger

logger = get_logger(__name__)

def business_method(self, ids, context={}):
    logger.info("Starting business operation", {
        'method': 'business_method',
        'record_count': len(ids),
        'user_id': get_active_user(),
        'company_id': get_active_company()
    })

    try:
        # Business logic
        result = self.do_operation(ids)

        logger.info("Operation completed successfully", {
            'result_count': len(result)
        })

        return result

    except Exception as e:
        logger.error("Operation failed", {
            'error': str(e),
            'traceback': traceback.format_exc()
        })
        raise

Metrics Collection

# Performance metrics
from netforce.metrics import track_performance

@track_performance('invoice.create')
def create(self, vals, context={}):
    # Automatically tracked: execution time, success/failure rate
    return super().create(vals, context)

# Business metrics  
def confirm_invoice(self, ids, context={}):
    for invoice in self.browse(ids):
        # Track business events
        metrics.increment('invoice.confirmed')
        metrics.histogram('invoice.amount', invoice.total_amount)
        metrics.gauge('invoice.backlog', self.get_draft_count())

Next Steps