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¶
- Discovery: Modules discovered in
nf_base/directory - Import: Python modules imported and models registered
- Layout Registration: XML layouts parsed and cached
- Action Registration: Actions linked to models and layouts
- Menu Building: Navigation structure assembled
- 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¶
- Explore detailed API Reference for JSON-RPC communication
- Learn Models for backend development
- Review Layouts for UI definition
- Check Frontend for React components
- Try the Quick Start Tutorial to build a module