Feature-rich MongoDB ODM and messaging framework for Java 21+
Available languages: English and Deutsch
- ποΈ High-performance object mapping with annotation-driven configuration
- π¨ Integrated message queue backed by MongoDB (no extra infrastructure)
- β‘ Multi-level caching with cluster-wide invalidation
- π Custom MongoDB wire-protocol driver tuned for Morphium
- π§ͺ In-memory driver for fast tests (no MongoDB required)
- π― JMS API (experimental) for standards-based messaging
- π Java 21 with virtual threads for optimal concurrency
Morphium is the only Java ODM that ships a message queue living inside MongoDB. If you already run MongoDB, you can power persistence, messaging, caching, and change streams with a single component.
| Feature | Morphium | Spring Data + RabbitMQ | Kafka |
|---|---|---|---|
| Infrastructure | MongoDB only | MongoDB + RabbitMQ | MongoDB + Kafka |
| Setup complexity | β Very low | βββ Medium | βββββ High |
| Message persistence | Built in | Optional | Built in |
| Message priority | β Yes | β Yes | β No |
| Distributed locks | β Yes | β No | β No |
| Throughput (internal tests) | ~8K msg/s | 10Kβ50K msg/s | 100K+ msg/s |
| Operations | β Very easy | ββ Medium | ββββ Complex |
* Numbers are indicative and depend heavily on hardware and workload.
- Documentation hub β entry point for all guides
- Overview β core concepts, quick start, compatibility
- Upgrade v6.1βv6.2 β migration checklist for 6.2.0
- Migration v5βv6 β step-by-step upgrade guide
- InMemory Driver Guide β capabilities, caveats, testing tips
- Optimistic Locking (
@Version) β prevent lost updates with@Version - SSL/TLS & MONGODB-X509 β encrypted connections and certificate-based authentication
- Aggregation examples:
docs/howtos/aggregation-examples.md - Messaging implementations:
docs/howtos/messaging-implementations.md - Performance guide:
docs/performance-scalability-guide.md - Production deployment:
docs/production-deployment-guide.md - Monitoring & troubleshooting:
docs/monitoring-metrics-guide.md
Morphium is now a multi-module project: morphium-parent (BOM), morphium (core library), and poppydb (server). The core library de.caluga:morphium no longer drags in server dependencies (Netty, etc.) β 90% leaner for users who just need the ODM.
The former MorphiumServer is now an independent module de.caluga:poppydb. It implements the MongoDB Wire Protocol as an in-memory server with Replica Set emulation, Change Streams, Aggregation Pipeline, and snapshot-based persistence.
PoppyDB and Morphium Messaging are optimized for each other β both sides recognize the counterpart and adapt their behavior, resulting in lower latency and less overhead than with a real MongoDB as messaging backend.
<dependency>
<groupId>de.caluga</groupId>
<artifactId>poppydb</artifactId>
<version>6.2.0</version>
<scope>test</scope> <!-- or remove scope for production use -->
</dependency>- β Full Wire Protocol: Any MongoDB client can connect (mongosh, Compass, PyMongo, ...)
- β Messaging Backend: Run Morphium messaging without MongoDB β optimized for low-latency
- β
CLI Tooling:
poppydb-6.2.0-cli.jarfor standalone deployment - β Replica Set Emulation: Test cluster behavior without real MongoDB
- β
Snapshot Persistence:
--dump-dir/--dump-intervalto preserve data across restarts
MorphiumDriverException extends RuntimeException β consistent with the MongoDB Java driver. Eliminates 40+ boilerplate catch-wrap-rethrow blocks.
@Reference now supports cascadeDelete and cascadeStore for automatic lifecycle management of referenced entities.
Annotation-driven auto-increment sequences β no manual counter management needed.
Works correctly with store() and storeList(), supports @CreationTime on Date, long, and String fields.
Morphium detects Azure CosmosDB connections and automatically adjusts behavior for compatibility.
See CHANGELOG for full details.
MorphiumDriverException extends RuntimeException instead of Exception. This eliminates boilerplate catch-wrap-rethrow blocks but requires attention in existing code:
// Multi-catch β simplify (MorphiumDriverException IS a RuntimeException now)
// Before:
catch (RuntimeException | MorphiumDriverException e) { ... }
// After:
catch (RuntimeException e) { ... }
// throws declarations β can be removed (but still compile if left in)
// Before:
public void doStuff() throws MorphiumDriverException { ... }
// After:
public void doStuff() { ... }
// Standalone catch β works unchanged
catch (MorphiumDriverException e) { ... } // still compilesThe embedded MongoDB-compatible server was extracted to its own module and renamed:
| 6.1.x | 6.2.0 | |
|---|---|---|
| Maven artifact | included in morphium |
separate: de.caluga:poppydb:6.2.0 |
| Package | de.caluga.morphium.server |
de.caluga.poppydb |
| Main class | MorphiumServer |
PoppyDB |
| CLI JAR | morphium-*-server-cli.jar |
poppydb-*-cli.jar |
| Test tag | @Tag("morphiumserver") |
@Tag("poppydb") |
If you use PoppyDB in tests, add the dependency:
<dependency>
<groupId>de.caluga</groupId>
<artifactId>poppydb</artifactId>
<version>6.2.0</version>
<scope>test</scope>
</dependency>Wire-protocol compatibility is preserved β PoppyDB responds to both poppyDB and morphiumServer in the hello handshake.
MorphiumConfig now organizes settings into typed sub-objects. The old setters still work but are @Deprecated:
// 6.1.x style (deprecated but functional)
cfg.setDatabase("mydb");
cfg.addHostToSeed("localhost", 27017);
// 6.2.0 style (preferred)
cfg.connectionSettings().setDatabase("mydb");
cfg.clusterSettings().addHostToSeed("localhost", 27017);
cfg.driverSettings().setDriverName("PooledDriver");Available sub-objects: connectionSettings(), clusterSettings(), driverSettings(), messagingSettings(), cacheSettings(), authSettings(), threadPoolSettings(), objectMappingSettings(), writerSettings().
The morphium core artifact no longer bundles server dependencies (Netty, etc.). If you only use Morphium as ODM, your dependency tree is ~90% leaner β no changes to your pom needed.
- Search for
catch (RuntimeException | MorphiumDriverExceptionβ simplify tocatch (RuntimeException - Search for
import de.caluga.morphium.serverβ replace withimport de.caluga.poppydb - Search for
MorphiumServerβ rename toPoppyDB - Search for
@Tag("morphiumserver")β rename to@Tag("poppydb") - Add
poppydbdependency if you use the embedded server in tests - Optional: migrate direct config setters to sub-object style
- Optional: adopt new features (
@Reference(cascadeDelete),@AutoSequence,@Version)
- Connect to MongoDB instances that require mutual TLS / x.509 client certificates
- Configure via
AuthSettings.setAuthMechanism("MONGODB-X509")together with the existingSslHelpermTLS setup
Prevents lost updates in concurrent environments without requiring pessimistic database locks. See docs/howtos/optimistic-locking.md for the full guide.
- Virtual threads for high-throughput messaging and change streams
- Pattern matching across driver and mapping layers
- Records: Not yet supported as
@Entityor@Embeddedtypes (see #116) - Sealed class support for cleaner domain models
- SSL/TLS Support: Secure connections to MongoDB instances (added in v6.0)
- Virtual threads in the driver for optimal concurrency
- Fewer duplicates thanks to refined message processing
- Virtual-thread integration for smoother concurrency
- Higher throughput confirmed in internal benchmarking
- Distributed locking for coordinated multi-instance deployments
- No MongoDB required for unit tests or CI pipelines
- Significantly faster test cycles in pure in-memory mode
- ~93% MongoDB feature coverage including advanced operations
- Full aggregation pipeline with
$lookup,$graphLookup,$bucket,$mergeObjects - MapReduce support with JavaScript engine integration
- Array operators including
$pop,$push,$pull,$addToSet - Change streams & transactions available for integration testing
- Drop-in replacement for most development and testing scenarios
- Complete rewrite of the guide set
- Practical examples and end-to-end use cases
- Dedicated migration playbook from 5.x to 6.x
- Architecture insights and best practices
- Java 21 or newer
- MongoDB 5.0+ for production deployments
- Maven
Maven dependencies:
<dependency>
<groupId>de.caluga</groupId>
<artifactId>morphium</artifactId>
<version>[6.2.0,)</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
<version>4.7.1</version>
</dependency>Migrating from v5? β docs/howtos/migration-v5-to-v6.md
<dependency>
<groupId>de.caluga</groupId>
<artifactId>morphium</artifactId>
<version>6.2.0</version>
</dependency>import de.caluga.morphium.Morphium;
import de.caluga.morphium.MorphiumConfig;
import de.caluga.morphium.annotations.*;
import de.caluga.morphium.driver.MorphiumId;
import java.time.LocalDateTime;
import java.util.List;
// Entity definition
@Entity
public class User {
@Id
private MorphiumId id;
private String name;
private String email;
private LocalDateTime createdAt;
// getters/setters
}
// Configuration
MorphiumConfig cfg = new MorphiumConfig();
cfg.connectionSettings().setDatabase("myapp");
cfg.clusterSettings().addHostToSeed("localhost", 27017);
cfg.driverSettings().setDriverName("PooledDriver");
Morphium morphium = new Morphium(cfg);
// Store entity
User user = new User();
user.setName("John Doe");
user.setEmail("[email protected]");
user.setCreatedAt(LocalDateTime.now());
morphium.store(user);
// Query
List<User> users = morphium.createQueryFor(User.class)
.f("email").matches(".*@example.com")
.sort("createdAt")
.asList();import de.caluga.morphium.messaging.MorphiumMessaging;
import de.caluga.morphium.messaging.Msg;
// Messaging setup
MorphiumMessaging messaging = morphium.createMessaging();
messaging.setSenderId("my-app");
messaging.start();
// Send a message
Msg message = new Msg("orderQueue", "Process Order", "Order #12345");
message.setPriority(5);
message.setTtl(300000); // 5 minutes
messaging.sendMessage(message);
// Receive messages
messaging.addListenerForTopic("orderQueue", (m, msg) -> {
// process order ...
return null; // no reply
});# Environment variables
export MONGODB_URI='mongodb://user:pass@localhost:27017/app?replicaSet=rs0'
export MORPHIUM_DRIVER=inmem
# System properties
mvn -Dmorphium.uri='mongodb://localhost/mydb' test
# Properties file (morphium.properties)
morphium.hosts=mongo1.example.com:27017,mongo2.example.com:27017
morphium.database=myapp
morphium.replicaSet=myReplicaSet# All tests
mvn test
# Full build with checks
mvn clean verify
# Tagged test selection
mvn test -Dgroups="core,messaging"
# Run against a real MongoDB instance
mvn test -Dmorphium.driver=pooled -Dmorphium.uri=mongodb://localhost/testdb# Default: in-memory driver (fast, no MongoDB required)
./runtests.sh
# Run tagged suites
./runtests.sh --tags core,messaging
# Parallel runs
./runtests.sh --parallel 8 --tags core
# Retry only failed methods
./runtests.sh --rerunfailed
./runtests.sh --rerunfailed --retry 3
# Single test class
./runtests.sh CacheTests
# Statistics
./runtests.sh --stats
./getFailedTests.sh # list failed methodsRun ./runtests.sh --help to see every option.
Tests are parameterized to run against multiple drivers. Use --driver to select:
# InMemory only (fastest, default)
./runtests.sh --driver inmem
# Against external MongoDB with all drivers (pooled + single + inmem)
./runtests.sh --uri mongodb://mongo1,mongo2/testdb --driver all
# Against external MongoDB with pooled driver only
./runtests.sh --uri mongodb://mongo1,mongo2/testdb --driver pooled
# Against PoppyDB (auto-starts local server)
./runtests.sh --poppydb --driver pooled # --morphium-server is a deprecated aliasComplete test coverage requires running against all backends:
# 1. Fast in-memory tests
./runtests.sh --driver inmem
# 2. Real MongoDB tests
./runtests.sh --uri mongodb://your-mongodb/testdb --driver all
# 3. PoppyDB tests
./runtests.sh --poppydb --driver pooled # --morphium-server is a deprecated aliasNew in v6.1
- β
Unified test base: All tests now use
MultiDriverTestBasewith parameterized drivers - β
Driver selection: Each test declares which drivers it supports via
@MethodSource - β Parallel safe: Tests isolated per parallel slot with unique databases
New in v6.0
- β
Method-level reruns:
--rerunfailedonly re-executes failing methods - β No more hangs: known deadlocks resolved
- β Faster iteration: noticeably quicker partial retries
- β Better filtering: class-name filters now reliable
Run ./runtests.sh --help to see every option.
TestConfig consolidates all test settings. Priority order:
- System properties (
-Dmorphium.*) - Environment variables (
MORPHIUM_*,MONGODB_URI) src/test/resources/morphium-test.properties- Defaults (localhost:27017)
The in-memory driver provides a largely MongoDB-compatible data store fully in memory:
Features
- β Full CRUD operations
- β Rich query operator coverage
- β
Aggregation stages such as
$match,$group,$project - β Single-instance transactions
- β Basic change streams
- β
JavaScript
$wheresupport
Performance
- Significantly faster than external MongoDB for tests
- No network latency
- No disk I/O
- Ideal for CI/CD pipelines
Usage
# All tests with the in-memory driver
./runtests.sh --driver inmem
# Specific tests
mvn test -Dmorphium.driver=inmem -Dtest="CacheTests"See docs/howtos/inmemory-driver.md for feature coverage and limitations.
PoppyDB (formerly MorphiumServer) runs the Morphium wire-protocol driver in a separate process, allowing it to act as a lightweight, in-memory MongoDB replacement.
Maven dependency (server module):
<dependency>
<groupId>de.caluga</groupId>
<artifactId>poppydb</artifactId>
<version>6.2.0</version>
</dependency>Building the Server
mvn clean package -pl poppydb -am -Dmaven.test.skip=trueThis creates poppydb/target/poppydb-6.2.0-cli.jar.
Running the Server
# Start the server on the default port (17017)
java -jar poppydb/target/poppydb-6.2.0-cli.jar
# Start on a different port
java -jar poppydb/target/poppydb-6.2.0-cli.jar --port 8080
# Start with persistence (snapshots)
java -jar poppydb/target/poppydb-6.2.0-cli.jar --dump-dir ./data --dump-interval 300Replica Set Support (Experimental)
PoppyDB supports basic replica set emulation. Start multiple instances with the same replica set name and seed list:
java -jar poppydb/target/poppydb-6.2.0-cli.jar --rs-name my-rs --rs-seed host1:17017,host2:17018Use cases
- Local development without installing MongoDB
- CI environments
- Embedded database for desktop applications
- Smoke-testing MongoDB tooling (mongosh, Compass, mongodump, ...)
Current limitations
- No sharding support
- Some advanced aggregation operators and joins still missing
See docs/poppydb.md for more details on persistence and replica sets.
Organizations run Morphium in production for:
- E-commerce: order processing with guaranteed delivery
- Financial services: coordinating transactions across microservices
- Healthcare: patient-data workflows with strict compliance
- IoT platforms: device state synchronization and command distribution
- Content management: document workflows and event notifications
- Blog: https://caluga.de
- GitHub: sboesebeck/morphium
- Issues: Report bugs or request features on GitHub
We appreciate pull requests! Areas where help is especially welcome:
- InMemoryDriver: expanding MongoDB feature coverage
- Documentation: tutorials, examples, translations
- Performance: profiling and benchmarks
- Tests: broader scenarios and regression coverage
How to contribute
- Fork the repository
- Create a feature branch from
develop(git checkout -b feature/AmazingFeature develop) - Commit your changes (
git commit -m 'Add AmazingFeature') - Push the branch (
git push origin feature/AmazingFeature) - Open a pull request against
develop(notmaster)
Important: master is only updated during releases. All PRs must target develop.
Tips
- Respect test tags (
@Tag("inmemory"),@Tag("poppydb")) - Run
./runtests.sh --tags corebefore submitting - Update documentation when you change APIs
Apache License 2.0 β see LICENSE for details.
Thanks to every contributor who helped ship Morphium 6.2.0 and to the MongoDB community for continuous feedback.
Questions? Open an issue on GitHub or browse the documentation.
Planning an upgrade? Follow the migration guide.
Enjoy Morphium 6.2.0! π
Stephan BΓΆsebeck & the Morphium team