03 / Architecture
Three modules, one contract.
Sumeru is not one repository you fork. It is a core Go module, a module of standard business addons, and your own workspace module that imports both.
Docs. Install and configuration live in
Documentation -> Installation.
Kernel details are under Concepts.
This page stays as a visual architecture overview.
sumeru_custom_addons
your workspace · runs the server
main.gosumeru.confaddonimports/addons/...
replace sumeru_addons
sumeru_addons
standard business addons
contactsproductcrmsale
sale_crmaccountpurchasehr
replace sumeru
sumeru
the core framework
ormmoduleengineserverevent
sdkschedulerapplogcache
in-tree apps: base · mail · automation · sumeru_ai
lib/pq
PostgreSQL
one database · many companies
Nothing imports sideways
No addon imports another addon's Go package. Invoicing subscribes to record.updated, checks
whether sale.order is in the registry, and only then builds an invoice.
Dependencies are declared, not imported
The depends array in the manifest drives install order and nothing else: sale
needs product, contacts and crm installed, but never links against
them.
// sumeru_addons/account/models/account_journal.go
package models
import "sumeru/core/sdk"
type AccountJournal struct {
sdk.BaseModel
}
func (AccountJournal) ModelName() string { return "account.journal" }
func (AccountJournal) Fields() []sdk.FieldDefinition {
return []sdk.FieldDefinition{
{Name: "name", Type: sdk.Char, String: "Journal Name", Required: true},
{Name: "code", Type: sdk.Char, String: "Short Code", Required: true},
{Name: "active", Type: sdk.Boolean, String: "Active", DefaultVal: true},
}
}
func init() {
sdk.RegisterModel(sdk.RegisterModelInput{Model: &AccountJournal{}, Module: "account"})
}
// sumeru_addons/account/manifest.json
{
"name": "account",
"display_name": "Invoicing",
"version": "1.0.0",
"depends": ["product", "contacts"],
"data": [
"security/security.xml",
"security/sys.access.csv",
"data/account_data.xml",
"views/account_views.xml",
"views/menus.xml"
],
"application": true
}
<!-- sumeru_addons/account/views/ -->
<action id="action_account_moves_out"
type="window" model="account.move"
name="Customer Invoices" view_mode="tree,form"/>
<menuitem id="menu_account_invoices" name="Invoices"
action="account.action_account_moves_out"/>
// sumeru_addons/account/init.go
func init() {
event.Subscribe("record.updated", onSaleOrderToInvoice)
}
func onSaleOrderToInvoice(ctx context.Context, ev event.Event) error {
if model, _ := ev.Payload["model"].(string); model != "sale.order" {
return nil
}
if _, ok := orm.Registry["sale.order"]; !ok {
return nil // Sales is not installed
}
// create draft invoice idempotently
return nil
}
# sumeru_addons/account/security/sys.access.csv
id,name,model,group_id,perm_read,perm_write,perm_create,perm_unlink
access_account_move_user,...,account.move,group_account_invoice,1,1,1,0
access_account_move_manager,...,account.move,group_account_manager,1,1,1,1