跪拜 Guibai
← All articles
Backend · Go · AI Programming

Ordering Guarantees and Extension Resolvers Tighten a Go Admin Base Layer

By 妙码生花 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Unordered database results are a silent data-corruption risk in any paginated API. Baking a primary-key fallback into the base layer removes a class of bugs that most projects never catch until production. The extension resolver pattern also decouples controller middleware from service signatures, letting teams pass session or tenant data without forking generated handler code.

Summary

Database-backed list queries now enforce an ORDER BY clause, defaulting to the primary key when no user sort is provided and always falling back to the primary key to prevent silent ordering drift across paginated results. A new BuildSerOpts method on the base controller collapses Omit, Select, and request parameters into a single call, cutting the List method setup from seven lines to one.

The base class also gains an ExtensionResolver — a function that accepts a Gin context and returns an arbitrary value — so controllers can inject custom session or business data into the service layer without rewriting handler methods. The resolver fires inside BuildSerOpts, populating a new Extension field on the service Options struct.

Finally, the Create path now automatically omits the primary key field from inserts when no explicit OmitFields are configured, using an existing model introspection method to identify the key.

Takeaways
Every list query now carries an ORDER BY clause; if the caller supplies none, the primary key is used, and the primary key always acts as a tiebreaker even when a user-specified sort field is present.
A new BuildSerOpts method on the base controller accepts an action name and a request struct, internally resolving Omit, Select, and primary key values so callers write one line instead of assembling Options by hand.
Service-layer Options gain an Extension field of type any, populated by an ExtensionResolver function (func(c *gin.Context) any) that controllers register via WithExtension.
Extension resolvers let controllers inject custom data like admin sessions into services without overriding base handler methods.
Create operations now automatically strip the primary key field from the insert payload when no OmitFields are explicitly configured, using a model introspection utility to detect the key.
Conclusions

Mandating a primary-key sort fallback at the framework level is a defensive design choice that eliminates an entire category of intermittent pagination bugs, yet it remains absent from most Go admin scaffolds.

Passing a resolver function rather than a static value for extension parameters keeps the base controller agnostic of concrete types while still allowing per-controller customization — a clean inversion-of-control pattern that avoids generics or interface bloat.

Collapsing Omit, Select, and request binding into BuildSerOpts trades explicit field-level visibility for call-site brevity; the risk is that future contributors may not realize which fields are being filtered unless the method name and action string convention are well-documented.

Concepts & terms
ORDER BY fallback
A defensive query pattern where the primary key is always appended as the final sort column, ensuring deterministic row ordering even when user-supplied sort fields contain duplicate values.
ExtensionResolver
A function with the signature func(c *gin.Context) any that a controller registers to produce custom data injected into the service layer's Options.Extension field, decoupling middleware concerns from base handler logic.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗