Last active
          August 20, 2025 19:25 
        
      - 
      
 - 
        
Save wware/45e6b4bbe15b48467de4024f56464ad7 to your computer and use it in GitHub Desktop.  
Revisions
- 
        
wware revised this gist
Aug 19, 2025 . 1 changed file with 56 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -179,4 +179,59 @@ The PostgreSQL container is still running: ```bash docker ps # Check if marten-postgres is running # Connect: docker exec -it marten-postgres psql -U postgres -d marten_test ``` --- ## OTHER STUFF Your gist captures the essential concepts very well! You've got all the important parts. I'd suggest adding just a few missing pieces to make it complete: **1. Add the actual code files** - You mention "Both java files go in src/main/java/com/example/" but don't show them. Include: ```java // User.java package com.example; import org.babyfish.jimmer.sql.*; import java.util.UUID; import java.time.LocalDateTime; @Entity public interface User { @Id UUID id(); String name(); String email(); LocalDateTime createdAt(); } ``` **2. Include the pom.xml** since that was crucial for getting Jimmer working: ```xml <!-- Key dependencies section --> <dependency> <groupId>org.babyfish.jimmer</groupId> <artifactId>jimmer-sql</artifactId> <version>0.8.138</version> </dependency> ``` **3. Add one important insight you discovered** - the "SQL backwards" LINQ concept: ``` LINQ is "SQL backwards": - SQL: SELECT name FROM users WHERE age > 25 ORDER BY name - LINQ: users.Where(u => u.Age > 25).OrderBy(u => u.Name).Select(u => u.Name) ``` **4. Mention the Docker PostgreSQL setup** since that was essential: ```bash docker run --name marten-postgres \ -e POSTGRES_PASSWORD=password \ -e POSTGRES_DB=marten_test \ -p 5432:5432 -d postgres:15 ``` Everything else is spot on! You've captured the key insights about document storage, code generation, and the conceptual similarities between Marten and Jimmer.  - 
        
wware created this gist
Aug 19, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,3 @@ .gradle/ target/ # btw the java files go in src/main/java/com/example/*.java This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,182 @@ # Marten Exploration Summary ## What is Marten? Marten is a document database library for .NET that uses PostgreSQL as its storage engine. It provides a NoSQL-like API while leveraging PostgreSQL's JSONB capabilities and ACID compliance. **Key Characteristics:** - Stores .NET objects as JSON documents in PostgreSQL JSONB columns - Provides LINQ query support that translates to PostgreSQL JSON operators - Supports event sourcing patterns - Uses optimistic concurrency control - Handles serialization with opinionated defaults (camel-case properties, enum strings) Both java files go in `src/main/java/com/example/` ## Core Concepts ### Document Storage Pattern Instead of traditional relational tables, Marten stores entire objects as JSON documents: ```sql -- Traditional relational approach CREATE TABLE users ( id UUID, name VARCHAR(100), email VARCHAR(100), created_at TIMESTAMP ); -- Marten approach CREATE TABLE users ( id UUID PRIMARY KEY, data JSONB NOT NULL -- entire object stored here ); ``` ### LINQ to PostgreSQL JSON Marten translates C# LINQ expressions into PostgreSQL JSON operators: ```csharp // C# LINQ session.Query<User>() .Where(u => u.Email.Contains("@example.com")) .OrderBy(u => u.Name) // Becomes PostgreSQL SELECT data FROM users WHERE data->>'email' LIKE '%@example.com%' ORDER BY data->>'name' ``` ### PostgreSQL JSON Operators - `->>` : Extract field as text (`data->>'email'`) - `->` : Extract field as JSON (`data->'address'`) - `@>` : Contains (`data @> '{"name": "Alice"}'`) - `?` : Key exists (`data ? 'email'`) ## Working Example: Raw PostgreSQL JSONB We built a simple example showing what Marten does under the hood: ```java // Store a document Map<String, Object> user = new HashMap<>(); user.put("name", "Alice"); user.put("email", "[email protected]"); String userJson = mapper.writeValueAsString(user); PreparedStatement insert = conn.prepareStatement( "INSERT INTO users (id, data) VALUES (?::uuid, ?::jsonb)"); insert.setString(1, UUID.randomUUID().toString()); insert.setString(2, userJson); // Query with JSON operators PreparedStatement query = conn.prepareStatement( "SELECT data FROM users WHERE data->>'email' LIKE ?"); query.setString(1, "%@example.com%"); ``` **Key insight:** Marten eliminates this JDBC/JSON boilerplate with a clean object-oriented API. ## Alternative: Jimmer (Java/Kotlin) Jimmer provides similar functionality to Marten for the JVM ecosystem: ### Entity Definition ```java @Entity public interface User { @Id UUID id(); String name(); String email(); LocalDateTime createdAt(); } ``` ### Object Creation ```java User user = UserDraft.$.produce(draft -> { draft.setName("Alice"); draft.setEmail("[email protected]"); draft.setCreatedAt(LocalDateTime.now()); }); ``` ### Code Generation Like Marten, Jimmer uses code generation at compile time: - Generates `UserDraft` for object creation - Generates `UserTable` for type-safe querying - Handles immutable object implementations ## Development Environment Setup ### Java/Jimmer Path (Working) ```bash # Prerequisites sudo apt install openjdk-17-jdk maven # Project structure mkdir marten-experiment && cd marten-experiment # Create pom.xml with Jimmer dependencies # Create entity interfaces # Maven handles code generation automatically mvn compile && mvn exec:java -Dexec.mainClass="com.example.JimmerExample" ``` ### .NET/Marten Path (Blocked) Corporate laptop restrictions prevented .NET SDK installation. Options to explore: - Request IT approval for .NET SDK - Use WSL2 with different permissions - Try Mono (outdated, not recommended) - Use Visual Studio if available ## Key Patterns Learned ### 1. Document vs Relational Storage - **Relational**: Normalize data across tables with foreign keys - **Document**: Store complete objects as JSON, query within documents - **Hybrid**: Marten/Jimmer allow both patterns in the same database ### 2. Code Generation Benefits - Define data shape once (class/interface) - Generated code handles: - Object creation/mutation - Serialization/deserialization - Type-safe query builders - Database schema management ### 3. Query Translation - Write queries in host language (C# LINQ, Java builder pattern) - Library translates to optimized SQL with JSON operators - Deferred execution - builds expression trees, executes when enumerated ## Event Sourcing Context Marten is popular for event sourcing because: - Events are naturally document-shaped (JSON) - PostgreSQL provides strong consistency guarantees - Built-in stream aggregation and projection capabilities - Can mix event data with traditional relational data ## Next Steps 1. **Get .NET working** - Either through IT approval or alternative approach 2. **Try real Marten examples** - Document storage, event sourcing, projections 3. **Compare with current architecture** - Understand migration implications 4. **Explore Marten's "warts"** - Concurrency handling, performance characteristics, serialization edge cases ## Working Code The Jimmer experiment is ready to extend: - Location: `~/marten-hack/` - Run: `mvn exec:java -Dexec.mainClass="com.example.JimmerExample"` - Generated code: `target/generated-sources/annotations/com/example/UserTable.java` - Add database operations once API is clarified The PostgreSQL container is still running: ```bash docker ps # Check if marten-postgres is running # Connect: docker exec -it marten-postgres psql -U postgres -d marten_test ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,58 @@ package com.example; import org.babyfish.jimmer.sql.JSqlClient; import org.babyfish.jimmer.sql.runtime.ConnectionManager; import java.sql.Connection; import java.sql.DriverManager; import java.time.LocalDateTime; import java.util.List; public class JimmerExample { public static void main(String[] args) throws Exception { // Simple connection manager with proper functional interface ConnectionManager connectionManager = new ConnectionManager() { @Override public <R> R execute(java.util.function.Function<Connection, R> block) { try (Connection conn = DriverManager.getConnection( "jdbc:postgresql://localhost:5432/marten_test", "postgres", "password")) { return block.apply(conn); } catch (Exception e) { throw new RuntimeException(e); } } }; // Create JSqlClient JSqlClient sqlClient = JSqlClient.newBuilder() .setConnectionManager(connectionManager) .build(); System.out.println("Jimmer setup complete - like Marten but for Java!"); System.out.println("UserTable class was generated at: target/generated-sources/annotations/com/example/UserTable.java"); // Let's just demonstrate that Jimmer is working by creating objects // We'll skip the database operations for now since the API is tricky // Create a user using Jimmer's generated draft (like Marten's object creation) User user = UserDraft.$.produce(draft -> { draft.setName("Alice"); draft.setEmail("[email protected]"); draft.setCreatedAt(LocalDateTime.now()); }); System.out.println("Created user with Jimmer:"); System.out.println("- Name: " + user.name()); System.out.println("- Email: " + user.email()); System.out.println("- Created: " + user.createdAt()); // This shows the key Marten-like concept: type-safe object creation // The actual database operations would be similar to what we did with raw JSONB System.out.println("\nThis demonstrates the core Marten pattern:"); System.out.println("1. Define entity interfaces"); System.out.println("2. Generated code handles the implementation"); System.out.println("3. Type-safe object creation and querying"); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,15 @@ package com.example; import org.babyfish.jimmer.sql.*; import java.util.UUID; import java.time.LocalDateTime; @Entity public interface User { @Id UUID id(); String name(); String email(); LocalDateTime createdAt(); } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,67 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>marten-hack</artifactId> <version>1.0</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Core Jimmer --> <dependency> <groupId>org.babyfish.jimmer</groupId> <artifactId>jimmer-sql</artifactId> <version>0.8.138</version> </dependency> <!-- PostgreSQL driver --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.3</version> </dependency> <!-- JSON processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- Annotation processing for Jimmer --> <dependency> <groupId>org.babyfish.jimmer</groupId> <artifactId>jimmer-apt</artifactId> <version>0.8.138</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <annotationProcessorPaths> <path> <groupId>org.babyfish.jimmer</groupId> <artifactId>jimmer-apt</artifactId> <version>0.8.138</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>