DEV Community

Cover image for Introducing Mitsuki - Spring Boot Enterprise Patterns and Speed with Python's Flexibility
David Landup
David Landup

Posted on

Introducing Mitsuki - Spring Boot Enterprise Patterns and Speed with Python's Flexibility

Introduction

Introducing Mitsuki: Spring Boot enterprise patterns meeting Python's flexibility and ecosystem! πŸš€πŸ

Mitsuki frontpage on GitHub

Get started in a single file with just a few lines, or have mitsuki init create your file structure, domain objects, and auto-implement repositories with CRUD operations. ✨

Quickstart with Mitsuki

It's a structured way to build web applications/REST APIs, and benefits from assumptions on that structure. You already know the patterns - Mitsuki brings them to your fingertips. πŸ’»

Architectural Patterns

Performance-wise - it brings your Python applications into the range of Java/JS applications on consumer hardware. πŸ“Š

Benchmark

Check it out on GitHub :)

Bootstrapping Projects in a Minute - from Zero to Functional Starter

Okay, let's go beyond "Hello World" - how long does it take to go from zero to something more functional? Something with a database connection, a domain object, a repository with CRUD capabilities, service and controller?

About a single minute. ✨

Here's a live example, of starting a Mitsuki project, which includes:

  • Project setup
  • Domain object
  • Entity controller, service and repository with functional CRUD

Gifs don't work here :(
Here's a link.

Automatic OpenAPI Generation

If you like Swagger, Redoc or Scalar - Mitsuki auto-generates all of these for you. You can disable any one of them, have them all, and choose a default UI to show at /docs.

Not a single line is required to get bootstrapped, and you can customize them easily:

Supercharging Data Layer Repositories: Auto-CRUD and Query DSL

Basic CRUD functionality is oftentimes boilerplate, especially in the first couple of steps of development. Basic SQL queries are also highly predictable.

Mitsuki includes Query DSL - which lets you write function names, and infers the SQL from them. Of course, you can also write your own queries in SQL (native), SQL (SQLAlchemy ORM syntax) or SQLAlchemy itself.

Given an @Entity, any @CrudRepository aware of an entity will allow you to just... run CRUD operations on it. save() it, find_by_id(), delete() them - no implementation needed:

@Entity()
@dataclass
class Post:
    id: int = Id()
    title: str = ""
    author: str = ""
    views: int = 0

@CrudRepository(entity=Post)
class PostRepository:
    # Built-in methods (auto-implemented):
    # - find_by_id(id) -> Post | None
    # - find_all(page=0, size=10) -> List[Post]
    # - save(entity: Post) -> Post
    # - delete(entity: Post) -> None
    # - count() -> int
    # - exists_by_id(id) -> bool
Enter fullscreen mode Exit fullscreen mode

Or, you want to find_by_field()? find_by_active_true()? Just define the method names - they get converted into SQL:

@CrudRepository(entity=Post)
class PostRepository:
    # Mitsuki turns these into SQL queries
    async def find_by_email(self, email: str): ...
    async def find_by_username(self, username: str): ...
Enter fullscreen mode Exit fullscreen mode
find_by_email(email) β†’ SELECT * WHERE email = ?
find_by_age_greater_than(age) β†’ SELECT * WHERE age > ?
count_by_status(status) β†’ SELECT COUNT(*) WHERE status = ?
Enter fullscreen mode Exit fullscreen mode

Or just write @Query implementations yourself:

@CrudRepository(entity=Post)
class PostRepository:

    @Query("""
            SELECT u FROM User u
            WHERE u.active = :active
            ORDER BY u.created_at DESC
        """)
    async def find_active_users(self, active: bool, limit: int, offset: int): ...
Enter fullscreen mode Exit fullscreen mode

Or use SQLALchemy:

@CrudRepository(entity=Post)
class PostRepository:
    # Or write custom SQLAlchemy
    async def find_popular_posts(self):
        async with self.get_connection() as conn:
            query = select(Post).where(Post.views > 1000)
            result = await conn.execute(query)
            return [dict(row._mapping) for row in result.fetchall()]
Enter fullscreen mode Exit fullscreen mode

Why Another Web Dev Framework?

I worked in both research and enterprise and have built a lot of services in Python and Java, and they converge to using similar patterns, regardless of how different the architectures and domains are (web apps, ML research, distributed systems, etc.)

After writing a lot of Python, I was missing a framework that strongly supported some of these patterns formally, and find that this structure lets you make assumptions that can really boost dev experience on long term projects.

Microframeworks are great. They let you get started with a single file of a few lines, but (in my opinion) lack the structure you want on long term projects, working with teams, so you end up making that structure yourself anyway. In doing so, you get a small initial boost in productivity, but at the cost of your productivity in the future.

Mitsuki tries to both allow you to start quick and easy with a single file in a few lines, but also be more friendly to you and your team through time, by giving structure to your development process.

Thus, I made an early version of a framework heavily inspired by Spring Boot. The core idea is that you can do enterprise apps without the enterprise pain, in Python, with high performance.

  • Want a simple REST API? app.py with a few lines.
  • Want a decent starter with auto-implemented CRUD? mitsuki init to get a starter project with domain classes, services, controllers and repositories.
  • Performance? Similar to Express and Spring Boot (in Docker, on an M1 MacBook Pro, 8GB of RAM), out of the box, no configuration needed.

Lightweight

Despite the "fancy sounding" terminology, Mitsuki itself is very lightweight, and only adds a very small overhead (10%) over the components that power it (namely, Starlette and Granian). I don't want to commit to ASGI only, and a future version will likely rewrite this core logic to leverage granian further.

There is a lot of ground left to cover, lots of docs to write, examples to explore, features to expand. I'm also planning to write a few tools that leverage the structure of the framework to increase DX within enterprise teams.

But before any of that, I'm looking for feedback.
Yay or nay? :)

Benchmarks

P.S. On the topic of performance and benchmarks, there are a few remarks in the repo's /benchmarks directory. Yes, most benchmarks are arbitrary, heavily gameable, and your bottleneck is likely going to be your business logic, not the framework, anyways. Yes, Spring Boot and Elysia will likely have higher ceilings, so running on a stronger CPU will likely change the order of the benchmark. Yes, there's a million variables that affect these. Yes, granian is written in Rust, not Python.

The point of the benchmark is threefold:

  • This is the sort of experience you get out of the box, on your device and where you'll deploy it (Dockerized on small instance most likely)
  • Python web apps can stand shoulder to shoulder with JS/Java performance-wise
  • Despite the seeming complexity around dependency injection, state tracking, etc., Mitsuki is pretty lightweight.

Top comments (0)