# Akash IKeeper vs Keeper: Understanding Module Interfaces in the Akash Network Node

> Explore Akash IKeeper vs Keeper module interfaces. Learn how IKeeper defines contracts for communication and Keeper implements stateful logic in the Akash Network node for loose coupling.

- Repository: [Akash Network/node](https://github.com/akash-network/node)
- Tags: internals
- Published: 2026-02-24

---

**The `IKeeper` interface declares the public contract for cross-module communication and testing, while the concrete `Keeper` struct implements stateful logic and store manipulation, with `NewKeeper` returning the interface type to enforce loose coupling.**

The akash-network/node repository organizes each Cosmos SDK module using a strict separation between interface and implementation. Understanding the distinction between **IKeeper** and **Keeper** is essential for contributing to the codebase or integrating with Akash's decentralized cloud infrastructure, as this pattern appears consistently across modules like `take`, `market`, `provider`, and `escrow`.

## The IKeeper Interface: Public Contracts

In [`x/take/keeper/keeper.go`](https://github.com/akash-network/node/blob/main/x/take/keeper/keeper.go), the `IKeeper` interface defines the module's public API without implementation details:

```go
type IKeeper interface {
    StoreKey() storetypes.StoreKey
    Codec() codec.BinaryCodec
    GetParams(ctx sdk.Context) (params types.Params)
    SetParams(ctx sdk.Context, params types.Params) error
    SubtractFees(ctx sdk.Context, amt sdk.Coin) (sdk.Coin, sdk.Coin, error)

    NewQuerier() Querier
    GetAuthority() string
}

```

This contract lists only method signatures, declaring what the module can do while hiding how it does it. Other modules import only this interface, preventing direct coupling to storage internals and allowing the implementation to evolve independently.

## The Keeper Struct: Stateful Implementation

Immediately following the interface in the same file, the concrete `Keeper` struct holds the actual state and dependencies:

```go
type Keeper struct {
    skey      storetypes.StoreKey
    cdc       codec.BinaryCodec
    authority string
}

```

The `NewKeeper` factory function constructs this struct but returns the interface type, encapsulating the concrete details:

```go
func NewKeeper(cdc codec.BinaryCodec, skey storetypes.StoreKey, authority string) IKeeper {
    return Keeper{
        skey:      skey,
        cdc:       cdc,
        authority: authority,
    }
}

```

Unlike the interface, the concrete `Keeper` contains unexported fields and implements full method bodies that manipulate the Cosmos SDK store, emit events, and perform business logic like fee calculation via internal helpers such as `findRate`.

## Key Differences Between IKeeper and Keeper

| Aspect | IKeeper | Keeper |
|--------|---------|--------|
| **Purpose** | Declares the public contract for cross-module interaction | Holds state (store keys, codec, authority) and implements logic |
| **Definition** | Method signatures only; no bodies | Full struct with fields and method implementations |
| **Return Type** | The type returned by `NewKeeper` | The type constructed inside `NewKeeper` |
| **Visibility** | Exported for other modules, tests, and mocks | Unexported fields; only interface methods are external |
| **Dependencies** | None; pure contract | References to other keepers (e.g., `EscrowKeeper` in market module) |

The **interface** captures *what* a module provides, while the **concrete keeper** determines *how* it manipulates the blockchain state.

## Dependency Injection and Cross-Module Usage

Modules interact through interface types rather than concrete imports, enabling loose coupling. For example, if a function requires access to the take module's fee logic, it accepts the interface:

```go
func ProcessOrder(ctx sdk.Context, takeKeeper takekeeper.IKeeper) {
    params := takeKeeper.GetParams(ctx)
    fees, remainder, err := takeKeeper.SubtractFees(ctx, payment)
    // Business logic proceeds without importing concrete keeper
}

```

Because the parameter is `takekeeper.IKeeper`, the calling module does not depend on the take module's internal storage layout or initialization logic.

### Testing with Mock Keepers

The interface pattern enables unit testing without a full blockchain state. Test files in `testutil/cosmos/mocks/` implement `IKeeper` with deterministic behavior:

```go
type MockTakeKeeper struct {
    keeper.Mock
    params types.Params
}

func (m *MockTakeKeeper) GetParams(sdk.Context) types.Params { 
    return m.params 
}

func (m *MockTakeKeeper) SubtractFees(_ sdk.Context, amt sdk.Coin) (sdk.Coin, sdk.Coin, error) {
    return amt, sdk.NewCoin(amt.Denom, sdk.ZeroInt()), nil
}

```

This mock satisfies `takekeeper.IKeeper`, allowing tests to verify cross-module logic without initializing KV stores or codecs.

## Module-Specific Interface Variations

While the pattern is consistent, each module's `IKeeper` reflects its specific responsibilities:

- **Take**: Focuses on fee management with `SubtractFees` and parameter getters/setters
- **Provider**: Provides CRUD operations like `Create`, `Update`, and `WithProviders` for provider records
- **Market**: Exposes complex lifecycle methods including `CreateOrder`, `CreateBid`, and `OnOrderMatched`, often holding references to `EscrowKeeper` for payment handling
- **Deployment/Escrow/Cert/Audit**: Standard CRUD patterns with module-specific state management through prefix stores

Each implementation resides in its respective `x/<module>/keeper/keeper.go` file, following the same structural conventions established in the take module.

## Summary

- **IKeeper** defines the public contract for Akash modules, enabling loose coupling and interface-based mocking
- **Keeper** implements stateful logic with access to store keys, codecs, and authority addresses required for Cosmos SDK operations
- **NewKeeper** returns the interface type, hiding concrete implementation details and preventing direct field access
- Cross-module communication relies exclusively on interface types, preventing circular dependencies and storage coupling
- Unit tests leverage the interface to inject mocks rather than initializing full keeper dependencies

## Frequently Asked Questions

### Why does Akash use IKeeper interfaces instead of concrete Keeper structs?

The interface pattern decouples modules, allowing developers to change internal storage mechanisms in [`x/take/keeper/keeper.go`](https://github.com/akash-network/node/blob/main/x/take/keeper/keeper.go) without affecting dependent modules. It also enables test mocking, as unit tests can implement `IKeeper` with deterministic behavior rather than spinning up a full Cosmos SDK application with database dependencies.

### Where is the IKeeper interface defined in the Akash codebase?

Each module defines its `IKeeper` interface at the top of the [`keeper/keeper.go`](https://github.com/akash-network/node/blob/main/keeper/keeper.go) file, immediately followed by the concrete `Keeper` struct. For example, see [`x/take/keeper/keeper.go`](https://github.com/akash-network/node/blob/main/x/take/keeper/keeper.go) lines 12-21 for the interface definition and lines 23-39 for the struct and factory function.

### How does NewKeeper hide the concrete implementation?

The `NewKeeper` factory function declares a return type of `IKeeper` (the interface) but constructs and returns a `Keeper` struct internally. This means callers receive an interface value and cannot access the unexported fields (`skey`, `cdc`, `authority`) of the concrete struct, enforcing encapsulation and preventing direct store manipulation outside the defined methods.

### Can the IKeeper interface vary between different Akash modules?

Yes, while all modules follow the same naming convention (`IKeeper` for interface, `Keeper` for struct), the method set varies based on module responsibility. The take module exposes fee-related methods like `SubtractFees`, while the market module includes lifecycle methods like `CreateOrder` and `OnOrderMatched`, reflecting each module's specific domain logic.