Giter VIP home page Giter VIP logo

MVCS

Introduction

MVCS is an application development approach that adds an additional layer to MVC -- the Service Layer. In the same way that MVC takes the position that you should separate model logic from view and controller logic, MVCS takes this notion a step further by advocating for application logic to live in the Service Layer, internally binding persistence to Entities for loose-coupling.

The main benefits of this approach are:

  • Persistence-agnosticism. The Persistence Layer can evolve and even be replaced without refactoring business logic.
  • Controller-agnosticism. The Service Layer can be exposed in multiple controllers. For example, the same "users" Service Layer can be used in multiple views, API versions, Thrift handlers, scripts, CLI programs, Kafka Ingesters, etc. without needing to duplicate code.
  • Framework-agnosticism. The Service Layer is developed "away" from the framework being used (e.g., Flask), so upgrading or replacing it is trivial.
  • Predictable development and testing patterns. Creates a common language for developing and testing different responsibilities. Abstracting persistence allows for test fixtures that don't need to interact with a database.
  • Collaborative language. Moves the conversation away from how to develop and test commonly recurring functionality, to how to organize responsibilities and how different layers of an application should interact.

The Service Onion

MVCS works by dividing a domain into a common set of layers, often called the "Service Onion":

Layer Responsibility
Entities Often referred to as "Domain Models", these classes represent business "things" - for example, a User in the Users Service. These objects are the language of the application. Their responsibility is to be a container (often called an Envelope) for the business object, to represent relationships to other business objects, to contain business logic related to these objects, and to optionally do validation & serialization of themselves. Entities are hydrated by the Service Layer by mapping raw data from Gateways & Repositories to them with Mappers.
Service Layer Classes These classes contain all the things your application can do, that is to say they contain all of your application's Application Logic. They are a way to "expose" your application to the Controller Layer and to other Service Layer Classes. They speak in Entities by hydrating them with raw data from Gateways & Repositories using Mappers. The Service Layer is the outer most layer of the Service Onion - you can imagine peeling back the layers, revealing the persistence internals. The Service Layer never leaks persistence details. In order to avoid overloading the term Services, which has come to mean API's in a distributed system, its better to use the term "Service Layer Class" or "Service Layer" when referring to this layer.
Repositories Contain queries to internal databases and returns raw data. This data is typically mapped to Entities by the Service Layer using Mappers. When using an ORM, Repositories tend to query against Data Store classes, whose only responsibility are to represent various tables and relationships between them. For each query needed in an application, there should be a correlating method on a Repository which executes the query and returns the raw data.
Gateways Sister-layer to Repositories - Gateways contains queries to external services (instead of internal databases) and return raw data. This data is typically mapped to Entities by the Service Layer using Mappers. Gateways tend to query against client libraries (like generated Thrift, for example) or network libraries. If needed, connection details may be represented in a Connection class, for example when configuring an instantiating an API client.
Mappers Responsible for mapping persistence to and from Entities.

It's important to note that the inner-most layers of the Service Onion must not know about layers that live above them. For example, Entities do not know about their own persistence or the Service Layer or view or jobs in the Controller Layer. This is the same for each layer in the Service Onion and is where most of the MVCS's decoupling benefits come from.

Directory Structure

On the filesystem, these layers tend to be named after their problem domain and then organized by responsibility.

#
# Entities
#
foo/entities/user.py    # contains User entities
foo/entities/comment.py # contains Comment entities

#
# Service Layer Classes
#
foo/services/user.py    # contains UserService
foo/services/comment.py # contains CommentService

#
# Repositories
#
foo/services/repositories/user.py    # contains UserRepository
foo/services/repositories/comment.py # contains CommentRepository

#
# Gateways
#
foo/services/gateways/comment.py  # contains CommentGateway

#
# Mappers
#
foo/services/mappers/user.py    # contains UserMapper
foo/services/mappers/comment.py # contains CommentMapper

Note that all persistence layer classes are stored as modules in the services directory. This is because persistence details should be hidden by the Service Layer.

Testing

MVCS not only gives you a vocabulary and method for organizing commonly recurring functionality - it also offers predictable testing patterns. The following diagram indicates how you should think about testing each layer. Notice how integration tests are pushed to the deepest layers of our application - this ensures our test suite is always fast, but also that our assumptions on internal databases and external services are correct.

Layer How to test
Entities Unit tests
Service Layer Classes Unit tests; Mock the persistence layer
Repositories Integration tests; Use fixtures to set up the state of the world
Gateways Integration tests; Use a vcr to record and replay the state of the world
Mappers Unit tests
Controllers Unit tests; Mock the service layer; Optional and very selective end-to-end-tests

NOTE: Your Service Layer Classes should never use objects that "you don't own" - for example, if a Service Layer Class uses an ORM's library directly (like SQLAlchemy's session) then you have no choice but to write integration tests since you should NEVER mock code that you don't own.

History / Related Reading

Employing Service Layers in application development stems from the marriage of DDD (dividing a problem area by its pieces and developing a common language to refer to those pieces) & SOLID/GRASP (dividing a technical problem into loosely coupled, composable, extensible, testable, and finite objects with OO). It is often seen in languages like Java (especially in Spring), ASP.NET, & PHP (especially in Symfony). The benefits are not language specific, and can be deployed in most settings.

Check out the following readings for more insight:

mvcs's Projects

mvcs-rust icon mvcs-rust

An RSS Reader Backend written in Rust with MVCS.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.