jardiscore/kernel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

jardiscore/kernel

Composer 安装命令:

composer require jardiscore/kernel

包简介

Hexagonal DDD kernel for PHP — DomainApp, BoundedContext, ContextResponse, DomainResponse; the runtime core that Jardis-generated domain code runs on

README 文档

README

Build Status Latest Version License: MIT PHP Version PHPStan Level PSR-12 Coverage PSR-3 PSR-11 PSR-14 PSR-16 PSR-18

Part of Jardis — the Domain-Driven Design platform for PHP. You model your domain; Jardis generates the production-ready hexagonal code (DTOs, Command/Query handlers, repositories, persistence). This package is part of the open-source foundation that generated code runs on.

The DDD kernel you always wanted. Eight files. Zero magic. Full control.

Why Jardis Kernel?

Most DDD frameworks force you into their world. Jardis Kernel gives you the hexagonal building blocks for Domain-Driven Design in PHP — DomainApp, BoundedContext, ConnectionPool — and stays out of the way.

  • new DomainApp() — done. No configuration, no YAML, no service files. Your domain boots itself.
  • Plain PDO works. Pass a PDO, get going. Need connection pooling later? Swap in a ConnectionPool. Same API.
  • Services shared across domains automatically. Five domains, one database connection. First-write-wins. Zero plumbing.
  • Every service is optional. Need a cache? Override one method. Don't need one? Don't touch anything.
  • ClassVersion built in. Versioned classes via namespace injection — the foundation for Jardis-generated code.
  • Immutable kernel. Once built, nothing changes. Safe for application servers, workers, long-running processes.

Installation

composer require jardiscore/kernel

Quickstart

1. Create your Domain

use JardisCore\Kernel\DomainApp;

class Ecommerce extends DomainApp
{
    public function order(): OrderContext
    {
        return new OrderContext($this->kernel());
    }
}

2. Define a Bounded Context

use JardisCore\Kernel\BoundedContext;
use JardisCore\Kernel\Response\DomainResponse;
use JardisCore\Kernel\Response\DomainResponseTransformer;

class PlaceOrder extends BoundedContext
{
    public function __invoke(): DomainResponse
    {
        $order = $this->payload();
        $pdo = $this->resource()->dbConnection();

        $stmt = $pdo->prepare('INSERT INTO orders (customer, total) VALUES (?, ?)');
        $stmt->execute([$order['customer'], $order['total']]);

        $this->result()->addData('orderId', (int) $pdo->lastInsertId());
        $this->result()->addEvent(new OrderPlaced($order));

        return (new DomainResponseTransformer())->transform($this->result());
    }
}

final class OrderPlaced
{
    public function __construct(public readonly array $order)
    {
    }
}

The context class is the API boundary — it wires named use-case methods to their handlers via context(), which sets the payload for the call chain:

class OrderContext extends BoundedContext
{
    public function placeOrder(array $order): DomainResponse
    {
        return $this->context(PlaceOrder::class, $order)();
    }
}

3. Use it

$shop = new Ecommerce();
$response = $shop->order()->placeOrder(['customer' => 'Acme', 'total' => 99.90]);

$response->isSuccess();   // true
$response->getData();     // ['PlaceOrder' => ['orderId' => 42]]
$response->getEvents();   // ['PlaceOrder' => [OrderPlaced {...}]]

That's it. No bootstrap file. No container setup. No framework.

Provide a Database

The simplest way — a plain PDO:

class Ecommerce extends DomainApp
{
    protected function dbConnection(): ConnectionPoolInterface|PDO|false|null
    {
        return new PDO('mysql:host=localhost;dbname=shop', 'root', '');
    }
}

That's all you need. A PDO. Works everywhere.

Provide Services

Override protected methods to add infrastructure. Every method uses three-state logic:

Return Meaning
object Use this service. Share it with other DomainApps (first-write-wins).
null No local service. Use the shared one from another DomainApp if available.
false Explicitly disabled. Don't use shared fallback either.
class Ecommerce extends DomainApp
{
    protected function dbConnection(): ConnectionPoolInterface|PDO|false|null
    {
        return new PDO('mysql:host=localhost;dbname=shop', 'root', '');
    }

    protected function logger(): LoggerInterface|false|null
    {
        return new MyLogger('/var/log/shop.log');
    }

    protected function classVersionConfig(): ClassVersionConfig
    {
        return new ClassVersionConfig(
            version: ['v1' => ['v1'], 'v2' => ['v2', 'current']],
            fallbacks: ['v2' => ['v1']],
        );
    }
}

Multi-Domain Service Sharing

Multiple domains in one application share services automatically:

$ecommerce = new Ecommerce();   // Builds PDO, registers it shared
$billing   = new Billing();     // Gets the same PDO — zero config
$analytics = new Analytics();   // Same. First-write-wins.

A domain that needs its own connection? Override the method. A domain that wants no connection at all? Return false.

Advanced: ConnectionPool (optional)

For application servers and read replicas, install jardisadapter/dbconnection and use ConnectionPool instead of plain PDO:

composer require jardisadapter/dbconnection
use JardisAdapter\DbConnection\ConnectionPool;
use JardisAdapter\DbConnection\Factory\ConnectionFactory;

class Ecommerce extends DomainApp
{
    protected function dbConnection(): ConnectionPoolInterface|PDO|false|null
    {
        $factory = new ConnectionFactory();

        return new ConnectionPool(
            writer: $factory->mysql('primary', 'user', 'pass', 'shop'),
            readers: [
                $factory->mysql('replica1', 'user', 'pass', 'shop'),
                $factory->mysql('replica2', 'user', 'pass', 'shop'),
            ],
        );
    }
}

ConnectionPool provides lifecycle management, health checks, round-robin load balancing, and automatic writer fallback when no readers are available. The rest of your code doesn't change.

Direct Kernel Usage

For full control without DomainApp, use DomainKernel directly:

use JardisCore\Kernel\DomainKernel;

$kernel = new DomainKernel(
    domainRoot: __DIR__ . '/src',
    connection: new PDO('mysql:host=localhost;dbname=shop', 'root', ''),
    logger: $myLogger,
    env: ['app_env' => 'production'],
);

$kernel->env('APP_ENV');     // 'production' (case-insensitive)
$kernel->dbConnection();     // PDO instance
$kernel->container();        // Factory (always available)

Architecture

DomainApp                       Entry point. Lazy bootstrap. Service sharing.
    ├── domainRoot()            Auto-detected via Reflection
    ├── classVersion()          Built from classVersionConfig()
    ├── cache()                 Three-state: object | null | false
    ├── logger()                   ↓
    ├── eventDispatcher()          ↓
    ├── httpClient()               ↓
    ├── dbConnection()             ↓
    ├── mailer()                   ↓
    ├── filesystem()               ↓
    ├── factory()               Factory + ClassVersion + DI Container
    └── loadEnv()               domainRoot/.env → private ENV

DomainKernel                    Immutable. Constructor injection only.
    ├── env(key)                Case-insensitive. Private > $_ENV
    ├── container()             Always Factory. Wraps external container.
    ├── cache()                 ?CacheInterface
    ├── logger()                ?LoggerInterface
    ├── eventDispatcher()       ?EventDispatcherInterface
    ├── httpClient()            ?ClientInterface
    ├── dbConnection()          ConnectionPoolInterface | PDO | null
    ├── mailer()                ?MailerInterface
    └── filesystem()            ?FilesystemServiceInterface

BoundedContext                  Use case handler.
    ├── handle(class, ...args)  Pass-through. Inherits caller's payload+version.
    ├── context(class, $p, $v)  Fresh context. Sets payload+version explicitly.
    │                           Used at API boundaries to start a new call chain.
    ├── resource()              Access to DomainKernel
    ├── payload()               Request data
    ├── version()               Active class-resolution version
    └── result()                Lazy ContextResponse

ContextResponse → DomainResponseTransformer → DomainResponse
    Mutable accumulator    Recursive aggregation    Immutable answer

Related Packages

Included dependencies:

Package Purpose
jardissupport/contract Interface contracts (DomainKernelInterface, etc.)
jardissupport/classversion Versioned class resolution via namespace injection
jardissupport/factory PSR-11 Container + class instantiation
jardissupport/dotenv ENV file loading

Optional (composer suggest):

Package Purpose
jardisadapter/dbconnection ConnectionPool with read/write splitting, health checks, load balancing

Kernel and Foundation

Jardis Kernel is the DDD core — it provides the building blocks but leaves service assembly to you. Its sister package Jardis Foundation (jardiscore/foundation) builds on top of Kernel and turns it into a ready-to-run platform:

Kernel Foundation
Approach Override protected methods, wire services yourself Everything configured via .env
Entry point class MyApp extends DomainApp class MyApp extends JardisApp
Services You build them Auto-assembled from ENV variables
Dependencies Minimal (4 packages + PSR interfaces) Full Jardis ecosystem (Cache, Logger, DbConnection, EventDispatcher, HTTP)
Use case Custom setups, libraries, testing Production DDD projects

When to use which: Start with Foundation for most projects — it handles all infrastructure wiring. Use Kernel directly when you need full control or want to integrate Jardis into an existing DI setup.

Runtime for Jardis-generated code

Both Kernel and Foundation serve as the runtime for the code Jardis generates — DDD project structures like Aggregates, BoundedContexts, Repositories, Commands, and Queries that run directly on these packages:

Jardis (development-time)
    │
    │  generates
    ▼
DDD Project Code (Aggregates, BoundedContexts, Repositories, ...)
    │
    │  runs on
    ▼
Foundation (JardisApp)  ──extends──▶  Kernel (DomainApp)
    ENV-driven                        Manual wiring
    Production-ready                  Full control

ClassVersion — built into the Kernel — is the foundation for versioned class resolution in Jardis-generated code. It enables namespace-based class versioning so generated code can evolve without breaking existing consumers.

Documentation

Full documentation, guides, and API reference:

docs.jardis.io/en/core/kernel

License

Jardis is open source under the MIT License. Free for any purpose — commercial or non-commercial.

Jardis — Development with Passion Built by Headgent Development

AI-Assisted Development

This package ships with a skill for Claude Code, Cursor, Continue, and Aider. Install it in your consuming project:

composer require --dev jardis/dev-skills

More details: https://docs.jardis.io/en/skills

jardiscore/kernel 适用场景与选型建议

jardiscore/kernel 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 194 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 03 月 29 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「php」 「domain」 「ddd」 「cqrs」 「kernel」 「code-generation」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 jardiscore/kernel 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 jardiscore/kernel 我们能提供哪些服务?
定制开发 / 二次开发

基于 jardiscore/kernel 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 194
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 36
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-29