Beego — Interfaces#

Interface catalog#

ControllerInterface#

  • Package: github.com/beego/beego/v2/server/web
  • File: server/web/controller.go:137
  • Methods:
    Init(ct *context.Context, controllerName, actionName string, app interface{})
    Prepare()
    Get()
    Post()
    Delete()
    Put()
    Head()
    Patch()
    Options()
    Trace()
    Finish()
    Render() error
    XSRFToken() string
    CheckXSRFCookie() bool
    HandlerFunc(fn string) bool
    URLMapping()
  • Purpose: Defines the contract for all HTTP controller handlers. The router stores registered controllers as ControllerInterface values, resolved at dispatch time via reflection.
  • Implementations: The base Controller struct implements all 16 methods. User controllers embed Controller and override individual HTTP verb methods.
  • Design quality: Broad — 16 methods is a lot for an interface. The HTTP verb methods (Get/Post/Delete/Put/Head/Patch/Options/Trace) return nothing and write to c.Ctx directly, which limits testability. The interface is defined by the provider (Controller) rather than the consumer, making it hard to swap. Violates ISP: a GET-only controller must satisfy the entire interface. The architecture compensates by providing default 405 implementations in Controller.

Configer#

  • Package: github.com/beego/beego/v2/core/config
  • File: core/config/config.go:56
  • Methods:
    Set(key, val string) error
    String(key string) (string, error)
    Strings(key string) ([]string, error)
    Int(key string) (int, error)
    Int64(key string) (int64, error)
    Bool(key string) (bool, error)
    Float(key string) (float64, error)
    DefaultString(key string, defaultVal string) string
    DefaultStrings(key string, defaultVal []string) []string
    DefaultInt(key string, defaultVal int) int
    DefaultInt64(key string, defaultVal int64) int64
    DefaultBool(key string, defaultVal bool) bool
    DefaultFloat(key string, defaultVal float64) float64
    DIY(key string) (interface{}, error)
    GetSection(section string) (map[string]string, error)
    Unmarshaler(prefix string, obj interface{}, opt ...DecodeOption) error
    Sub(key string) (Configer, error)
    OnChange(key string, fn func(value string))
    SaveConfigFile(filename string) error
  • Purpose: Uniform typed key-value access over any configuration backend. The BaseConfiger struct provides default implementations of all numeric/bool methods in terms of a single reader func(ctx, key) (string, error), so driver authors only need to implement the reader.
  • Implementations: ini, json, yaml, toml, xml, env, etcd — seven drivers registered via the companion Config adapter interface’s Parse method.
  • Design quality: Wide but internally structured: paired Foo/DefaultFoo methods for every type create an API suitable for both strict error-checking and defensive fallback access. Sub (returns a namespaced sub-configer) and OnChange (live-reload callback) are modern additions. BaseConfiger reduces driver boilerplate significantly.

Config (factory interface)#

  • Package: github.com/beego/beego/v2/core/config
  • File: core/config/config.go:200
  • Methods:
    Parse(key string) (Configer, error)
    ParseData(data []byte) (Configer, error)
  • Purpose: Factory interface for parsing raw config files or bytes into a Configer. Separates file-format parsing from value access — a two-level abstract factory.
  • Implementations: IniConfig, JSONConfig, YAMLConfig, TOMLConfig, XMLConfig, ENVConfig, EtcdConfig — each registered via Register(name, Config).
  • Design quality: Exemplary ISP — only 2 methods, tightly focused. The naming is confusing (Config interface vs Config struct elsewhere in server/web) but the design itself is clean.

Ormer / TxOrmer (ORM interface family)#

  • Package: github.com/beego/beego/v2/client/orm
  • File: client/orm/types.go:258,263
  • Interface hierarchy:
    DQL (read operations: Read, ReadOrCreate, LoadRelated, QueryTable, QueryM2M, …)
    DML (write operations: Insert, Update, Delete, Raw, …)
    DriverGetter
      └─ ormer (private: DQL + DML + DriverGetter)
           └─ QueryExecutor (public alias)
                ├─ Ormer = QueryExecutor + TxBeginner
                └─ TxOrmer = QueryExecutor + TxCommitter
  • Methods (Ormer top-level additions):
    // Via TxBeginner:
    Begin() (TxOrmer, error)
    BeginWithCtx(ctx context.Context) (TxOrmer, error)
    BeginWithOpts(opts *sql.TxOptions) (TxOrmer, error)
    BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error)
    DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error
    DoTxWithCtx/WithOpts/WithCtxAndOpts variants
  • Purpose: Ormer is the main entry point for all database operations. TxOrmer scopes those same operations within a transaction and adds Commit/Rollback/RollbackUnlessCommit.
  • Implementations: ormBase (internal struct). The mock package provides MockOrm via client/orm/mock.
  • Design quality: The DML/DQL decomposition is thoughtful — both carry identical WithCtx variants for every method, making context propagation consistent but verbose (nearly doubling method count). The DoTx closure pattern is a standout: it handles Begin/Commit/Rollback automatically, reducing transaction boilerplate. The private ormer / public QueryExecutor indirection adds unnecessary complexity for little gain.

QuerySeter#

  • Package: github.com/beego/beego/v2/client/orm
  • File: client/orm/types.go:276
  • Methods (selected):
    Filter(string, ...interface{}) QuerySeter
    FilterRaw(string, string) QuerySeter
    Exclude(string, ...interface{}) QuerySeter
    SetCond(*Condition) QuerySeter
    Limit(limit interface{}, args ...interface{}) QuerySeter
    Offset(offset interface{}) QuerySeter
    GroupBy(exprs ...string) QuerySeter
    OrderBy(exprs ...string) QuerySeter
    ForceIndex/UseIndex/IgnoreIndex(indexes ...string) QuerySeter
    RelatedSel(params ...interface{}) QuerySeter
    Distinct() QuerySeter
    ForUpdate() QuerySeter
    Count() (int64, error)
    Exist() bool
    Update(values Params) (int64, error)
    Delete() (int64, error)
    PrepareInsert() (Inserter, error)
    All(container interface{}, cols ...string) (int64, error)
    One(container interface{}, cols ...string) error
    Values/ValuesList/ValuesFlat variants
    Aggregate(s string) QuerySeter
    // plus WithCtx variants for all terminal operations
  • Purpose: Fluent query builder. Returns self (QuerySeter) for chaining; terminal methods (Count, All, One, Delete, Update) execute the query. Uses Django-style ORM lookups (profile__age__gt).
  • Implementations: querySet (internal struct). Also mocked via client/orm/mock.
  • Design quality: Very large interface (~40+ methods including WithCtx variants). The fluent return of QuerySeter from filter/sort methods is idiomatic and expressive. The Django-style double-underscore field traversal is unusual in Go but powerful for related model lookups. The massive size makes it hard to mock or implement from scratch.

Cache#

  • Package: github.com/beego/beego/v2/client/cache
  • File: client/cache/cache.go:52
  • Methods:
    Get(ctx context.Context, key string) (interface{}, error)
    GetMulti(ctx context.Context, keys []string) ([]interface{}, error)
    Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error
    Delete(ctx context.Context, key string) error
    Incr(ctx context.Context, key string) error
    Decr(ctx context.Context, key string) error
    IsExist(ctx context.Context, key string) (bool, error)
    ClearAll(ctx context.Context) error
    StartAndGC(config string) error
  • Purpose: Universal cache backend contract. The Instance func() Cache type and Register(name, Instance) + NewCache(name, config) idiom follows beego’s universal driver-registry pattern.
  • Implementations: memory, file, redis (go-redis), memcache, ssdb — each in a sub-package registered via func init() import side-effect.
  • Design quality: Well-sized (9 methods). All operations take context.Context. StartAndGC is the only lifecycle method. The interface{} return type predates generics; a future version could use any or parameterize on value type.

session.Store#

  • Package: github.com/beego/beego/v2/server/web/session
  • File: server/web/session/session.go:46
  • Methods:
    Set(ctx context.Context, key, value interface{}) error
    Get(ctx context.Context, key interface{}) interface{}
    Delete(ctx context.Context, key interface{}) error
    SessionID(ctx context.Context) string
    SessionReleaseIfPresent(ctx context.Context, w http.ResponseWriter)
    SessionRelease(ctx context.Context, w http.ResponseWriter)
    Flush(ctx context.Context) error
  • Purpose: Per-request session data access. SessionRelease serializes and persists the session back to the provider at the end of a request; SessionReleaseIfPresent is a conditional variant for lazy-start sessions.
  • Implementations: cookie, file, memory, redis, mysql — registered via Register(name, Provider).
  • Design quality: Focused, context-aware. The http.ResponseWriter parameter in SessionRelease is a leaky abstraction — the store must write the cookie header directly, coupling the storage layer to the HTTP layer.

session.Provider#

  • Package: github.com/beego/beego/v2/server/web/session
  • File: server/web/session/session.go:58
  • Methods:
    SessionInit(ctx context.Context, gclifetime int64, config string) error
    SessionRead(ctx context.Context, sid string) (Store, error)
    SessionExist(ctx context.Context, sid string) (bool, error)
    SessionRegenerate(ctx context.Context, oldsid, sid string) (Store, error)
    SessionDestroy(ctx context.Context, sid string) error
    SessionAll(ctx context.Context) int
    SessionGC(ctx context.Context)
  • Purpose: Global session storage management. The Manager struct holds a Provider and delegates all store-level operations through it. Provider is a backend (redis, file, mysql, memory); Store is a per-request handle.
  • Design quality: Good two-level separation (provider ↔ per-session store). The SessionAll method returning an int count is a hint that active-session monitoring is built in. JSON config string in SessionInit is a weak spot — opaque to static analysis.

Logger#

  • Package: github.com/beego/beego/v2/core/logs
  • File: core/logs/log.go:83
  • Methods:
    Init(config string) error
    WriteMsg(lm *LogMsg) error
    Destroy()
    Flush()
    SetFormatter(f LogFormatter)
  • Purpose: Log backend adapter. The BeeLogger struct fans out log messages to multiple registered Logger adapters concurrently via goroutines and channels.
  • Implementations: console, file, multifile, smtp, elasticsearch, alils (Alibaba Cloud Log Service).
  • Design quality: Minimal and stable. Init takes a JSON config string (same weakness as session). SetFormatter allows runtime log-line customization. No context.Context on WriteMsg — consistent with the pre-context era of beego’s logging layer.

LifeCycleCallback#

  • Package: github.com/beego/beego/v2/server/web
  • File: server/web/server.go:81
  • Methods:
    AfterStart(app *HttpServer)
    BeforeShutdown(app *HttpServer)
  • Purpose: Hook for external code to react to server start and stop events. Registered via HttpServer.AddLifeCycleCallback(cb).
  • Design quality: Simple and targeted. Passing *HttpServer gives callbacks access to the full server, which is both powerful and a potential source of misuse (e.g., calling Run() recursively). A narrower interface exposing just Config or a read-only view would be safer.

FilterHandler#

  • Package: github.com/beego/beego/v2/server/web
  • File: server/web/router.go:82
  • Methods:
    Filter(*beecontext.Context) bool
  • Purpose: Predicate for the access log filter: if Filter returns true, the request is excluded from the access log. Also used internally by DefaultAccessLogFilter.
  • Design quality: Textbook single-method interface, easily satisfiable. Named like a handler but acts as a predicate — the naming is slightly misleading.

Tasker#

  • Package: github.com/beego/beego/v2/task
  • File: task/task.go:105
  • Methods (key):
    GetSpec(ctx context.Context) string
    GetStatus(ctx context.Context) string
    Run(ctx context.Context) error
    SetNext(ctx context.Context, t time.Time)
    GetNext(ctx context.Context) time.Time
    SetPrev(ctx context.Context, t time.Time)
    GetPrev(ctx context.Context) time.Time
    GetTimeout(ctx context.Context) time.Duration
  • Purpose: Scheduled task abstraction. The TaskManager stores Tasker values and drives them in a goroutine timer loop.
  • Implementations: Task struct. The Option interface (also in task.go) provides a functional-options extension point for task configuration.
  • Design quality: Context-aware throughout. 8 methods covering both state inspection and lifecycle. The paired Set/Get methods for Next/Prev time are clear. Wider than strictly necessary — a simpler Run(ctx) error would suffice for the executor, with state queries separated.

Interface patterns#

  • Size distribution: Wide variation. Single-method interfaces (FilterHandler, Config factory) sit alongside 16-method monsters (ControllerInterface) and 40+-method query builders (QuerySeter). The ORM layer is the outlier; core infrastructure interfaces (Cache, Logger, Store) are well-sized (5–9 methods).

  • Embedding: The ORM uses deep composition: Ormer embeds QueryExecutor which embeds the private ormer which embeds DQL + DML + DriverGetter. This creates a clean public surface while hiding internal layering. No other subsystem uses interface embedding to the same degree.

  • Implicit satisfaction: Beego follows Go convention — interfaces are defined at the point of use (ControllerInterface in the router package, FilterHandler in the routing package) or at the boundary package (all core/ interfaces). Implementations in driver sub-packages satisfy them implicitly, with no explicit var _ Cache = (*MemoryCache)(nil) guards visible in the main packages (though they may appear in tests).

  • stdlib interfaces used:

    • http.HandlerControllerRegister implements it; MiddleWare wraps it
    • http.ResponseWritersession.Store takes it directly (leaky abstraction)
    • fmt.Stringer — not pervasive; beego prefers explicit String() methods
    • io.ReadCloser, io.Writer — used internally in context/response handling
  • Registry pattern: Universally applied across all plugin subsystems. Each domain uses the same idiom:

    Register(name string, impl DriverInterface)
    NewXxx(name, jsonConfig string) (Interface, error)

    This pattern appears in core/config, core/logs, client/cache, server/web/session, and client/orm.


Key abstractions#

  1. Configer — The backbone of beego’s multi-format configuration system. Its BaseConfiger adapter base is a textbook use of a partial implementation struct to reduce boilerplate across 7+ driver implementations. The Sub and OnChange methods enable namespaced and reactive config, setting it apart from simpler key-value stores.

  2. Ormer + TxOrmer — The cleanest interface decomposition in the project. The DML/DQL split provides a principled separation of read vs. write concerns; the Ormer/TxOrmer split makes transaction scope visible in the type system — you cannot accidentally begin a transaction on a TxOrmer or commit without one on an Ormer. The DoTx closure is the most idiomatic addition.

  3. ControllerInterface — The architectural linchpin of the MVC system. Reflection-based dispatch through this interface allows the auto-router to work (URL → controller type → HTTP-method method by name convention). Its breadth is a deliberate trade-off: users extend Controller rather than implement ControllerInterface from scratch, so the 16-method size is rarely felt directly.

  4. Cache — The clearest example of beego’s driver-registry pattern applied well. 9 focused methods, full context propagation, and a clean Register/NewCache factory. Serves as the template for understanding how every other pluggable subsystem works.

  5. session.Store + session.Provider — The two-level session abstraction (global lifecycle vs. per-request handle) is architecturally sound. The pattern recurs conceptually in ORM (Ormer vs. QuerySeter) and config (Config factory vs. Configer reader).


Interface-driven extensibility#

Beego’s entire plugin ecosystem is driven by the register-then-retrieve interface pattern:

init() import side-effect  →  Register(name, factory)
user code                  →  NewXxx(name, jsonConfig)  →  Interface

This means adding a new cache backend, log adapter, session store, or config format requires:

  1. Implementing the relevant interface
  2. Registering it via Register in an init() function
  3. Users import the driver package for its side effect

The pattern is consistent enough to be considered beego’s primary extension mechanism. It predates Go modules and the explicit dependency era, using blank imports (_ "github.com/beego/beego/v2/client/cache/redis") as the activation mechanism — a design that remains valid but is opaque compared to explicit dependency injection.

The core/bean package adds an optional reflection-based IoC container (AutoWireBeanFactory interface) for struct-level dependency injection via inject: tags, but it is not used by the framework internals and represents a separate, less-integrated extension point.