[Docs] When to choose Zend\Expressive over Zend\Mvc #283

Closed
RalfEggert opened this Issue Jan 25, 2016 · 18 comments

Projects

None yet
@RalfEggert
Contributor

In the "Announcing the Zend Framework 3 Roadmap" blog entry, someone asked an interesting question which probably will be asked by many others.

http://framework.zend.com/blog/announcing-the-zend-framework-3-roadmap.html#comment-2476333814

Thank you Ralf for your answer ;) . Another question, is there a guideline to
check which method i can use, because right now i do not know which
method (Zend\Mvc or Zend\Expressive) i can choose.

I will reask here: When should someone choose Zend\Expressive and when Zend\Mvc?

Any ideas?

I will be glad to sum it up for the docs. Maybe could be added to the Zend\Mvc docs as well

@xtreamwayz
Member

I've been asking this question myself a lot. This is what I come up with so far:

  • Expressive for small apps, api's or if you need complete control over structure, packages, etc.
  • Mvc if you need a full framework and it does exactly what you need.

With expressive you can do anything you want. However development time may be longer since you need to add other packages as needed. You can use any package you want and mix and match the way you prefer. At first you will miss the convenience of the predefined functionality like helper functions in controllers etc, but over time you can add this yourself if needed.

So basically with Expressive you can be your own architect whereas using Mvc you are sort of bound to its conventions.

@RalfEggert
Contributor

To sum up:

Zend\Expressive => micro framework
Zend\Mvc => full-stack framework

@mtymek
Contributor
mtymek commented Jan 25, 2016

It's not necessarily that simple - Expressive is also great if you want to build something bigger and more customized. I have an application that runs Expressive on top of massive legacy ZF1 application and so far it works great - I wouldn't be able to achieve the same with ZF2.

For me it looks like this:

Zend\Expressive => micro framework for both beginners and experts
Zend\Mvc => default choice if you want to build something "generic" (CRUD, etc) - for both beginners and experts
Zend\Expressive again => for something non-standard and/or complex, if you are an expert.

@weierophinney
Member

I'm struggling to answer this question myself, and have actually been tasked by our services team to come up with an answer.

The reason I struggle is with this assertion by @mtymek:

Zend\Mvc => default choice if you want to build something "generic" (CRUD, etc) - for both beginners and experts

The problem is that zend-mvc is anything but beginner friendly at this point. You're required to deep dive into the event manager, service manager, and module system essentially from the outset, and these require more than a passing understanding of OOP and design patterns.

I also think this statement by @xtreamwayz is interesting:

Mvc if you need a full framework and it does exactly what you need.

The problem with full-stack frameworks is that they make a ton of assumptions, and, being general purpose, tend to never do exactly what you need unless your application is in the specific set of use cases the framework was designed to address. This can be summed up in one sentence: The full-stack framework is your architecture. For Rails, that application was a blog and/or basecamp itself. For ZF, we never had a specific use case, though with ZF2, we've been leaning towards APIs and/or CMS development. This has largely meant that to be suitable for any given application, you have to modify the workflow to suit your needs.

Micro-frameworks typically address either the API space or deliberately don't address any specific use cases, and instead aim to be a tool to use as a part of your architecture, but not as the architecture itself.

With Expressive, we've been keeping the following in mind:

  • minimal architecture; any decisions made are made with flexibility in mind. Expressive is a tool for creating applications, but not the architecture.
  • APIs; we want to build the next generation of Apigility on it.
  • piping; we want to embrace the unix philosophy for the web, and envision using many small, single-purpose middlewares to build complex behavior.

That said, it's also providing architecture: everything is middleware, and you use piping to enable complex behavior. However, unlike a full-stack framework, the developer gets to choose what pieces become a part of that pipeline, versus having that pipeline pre-defined. I suspect that is the key difference to message.

@bakura10
Contributor

It's definitely a new way of thinking. In ZF2 customization was actually really hard. You had to understand the various events to be able to add your own code before the controller, or after, which ended with a very complex workflow.

In Expressive, I actually don't use event manager (it's not even part of my dependencies): the workflow is explicitly defined by the middleware piping and definitely makes things sooooo much easier to understand that I actually find Expressive much more suited for bigger apps or complex apps. Need a custom authentication mechanism for some endpoints? Just add a middleware only for those paths.

After trying Expressive I'm having a hard time finding where the Zend\Mvc can be superior. It is less flexible, less performant, harder to follow.

For me the only reason to use Zend\Mvc (and, therefore, the eco-system around it) is the facilities provided by the module eco-systems. But even in that case, I've found out that for that, the middleware philosophy makes it so much easier. You no longer need to install Zend\Authentication that would try to map into the mvc, spending a lot of time how it works... Want an authentication? Just analyze your need, and boom, ten lines letter, it's done:

class AuthenticationMiddleware
{
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $out)
    {
        $token = $request->getHeaderLine('Authorization');

        if (empty($token)) {
            return $response->withStatusCode(401);
        }

        try {
            $user = $this->userService->getByToken($token);
        } catch (NotFoundException $exception) {
            return $response->withStatusCode(401);
        }

        $request = $request->withAttribute('logged_user', $user->getId());

        return $out($request, $response);
    }
}
@vrkansagara

@bakura10 It's cool and very deep answer given by the @weierophinney . Thanks a lot guys and keep updating this talk. πŸ‘

@pdrosos
pdrosos commented Jun 12, 2016

Thank you for this discussion!

I am currently starting my first Expressive application, which should be REST API for a mobile app + admin application, used from a couple of different user roles to add/change data, send push notifications to mobile app users etc.

I am currently considering to develop both the API and the admin application with Expressive, using the possibility for path segregation and different middleware pipe for different paths (/admin and /api). I would like to reuse doctrine entities, repositories and services in the admin application and in the REST API.

Another idea I have is to make the admin application with ZF2, which we have used for a couple of other similar applications, with Doctrine and modules like ZfcUser and ZfcRbac. And then reuse the entities, repositories and services from the ZF2 application and make the REST API with Expressive, which is more suitable for this purpose.
Problem is that I don't know how exactly to connect Expressive application with ZF2 application, so Expressive to be able to reuse ZF2 entities and services and serve like a router and REST API layer + OAuth2 authentication only.

@mtymek you mention you have used Expressive on top of ZF1 application. Could you please share how did you connect them and reused the ZF1 code in Expressive?

@weierophinney @xtreamwayz @bakura10 could you please share opinions what architecture is better for this use case and is the combination of Expressive for REST API + ZF2 for application logic possible?

@xtreamwayz
Member

You can use piping to run different applications based on paths.

$app = AppFactory::create();
$app->pipe('/blog', new WordPressMiddleware());
$app->pipe('/api', new ExpressiveApiMiddleware());
$app->pipe('/admin', new ZF2AdminMiddleware());
$app->pipe('/store', new MagentoMiddleware());
$app->run();

And inside those middleware you start the application. In theory you can share the container between your different apps, especially with Expressive and ZF2.

To reuse entities I create a Domain namespace (sort of module). It contains only the entities and repositories. I can then reuse those between other modules. But if you share the zf2 and expressive config in the same container you can probably access those anyway without moving them into a Domain module (disclaimer: I haven't tried this yet).

@mtymek
Contributor
mtymek commented Jun 12, 2016 edited

@pdrosos

My idea was to setup up Expressive "before" ZF1 app. If Expressive is not able to route the request, it is then passed to underlying ZF1 code. This allowed us to benefit from PSR-7 for new parts of the platform, without rewriting any of the legacy ZF1 codebase.

Initially I wanted covert PSR-7 request into Zend_Controller_Request_Http, and create a middleware that wraps ZF1 application. I found a few issues here:

  • ZF1 controllers tend to use "redirect and exit", which stops application, making it impossible to emit PSR-7 response.
  • my ZF1 app has a few legacy controllers where we disable view renderer and layout, and then simply echo content, or use PHP header() function directly.
  • ZF1's Request object is based on superglobals ($_GET, $_POST...)

Because of the above, I ended up with a bit "hacky" code. I created my own wrapper around Zend\Expressive\Application class.
I built a "pseudo-middleware" for ZF1 app (it is not a real middleware because it ignores PSR-7 request & response), and piped it into wrapped Expressive application:

public function run()
{
    $this->expressiveApp->pipe('/', [$this, 'zf1AppMiddleware']);
    // ...
}

My middleware simply bootstraps and runs legacy application:

public function zf1AppMiddleware(ServerRequestInterface $request, ResponseInterface $response)
{
    $application = new Zend_Application(
        APPLICATION_ENV,
        $this->container->get('config')
    );
    $zf1App->bootstrap();
    $zf1App->run();
    return new Zf1Response();
}

Zf1Response is a "dummy" class. If it is returned, then Expressive emitter is skipped:

$request  = ServerRequestFactory::fromGlobals();
$app = $this->expressiveApp;
$response = $app($request, new Response());
if ($response instanceof Zf1Response) {
    // ZF1 app - output is already sent
    return;
}
$emitter = new SapiEmitter();
$emitter->emit($response);

It is more or less how it works. I didn't have to worry about reusing code written for ZF1 in Expressive parts, because we've been already using ServiceManager as dependency container, and both application simply share the configuration.
As you see, it is not perfect, but it is enough until we refactor old stuff.

@pdrosos
pdrosos commented Jun 14, 2016 edited

@xtreamwayz @mtymek Thank you for the ideas and the useful feedback!

It's interesting approach to mix applications in this way. I will probably try it with ZF2 and Expressive later, if some of our existing ZF2 projects needs an API. For my new project I decided to stay with Expressive both for API and admin web app. The things I miss the most are the ZfcUser and ZfcRbac modules, which are currently tightly coupled with ZF2 MVC. But middleware allows simpler implementation of authentication and authorization according to specific project requirements and I start liking Expressive more and more as long as work with it and get used to it.

@settermjd
Contributor
settermjd commented Jan 10, 2017 edited

After creating a couple of projects with Zend\Expressive, my default preference is to choose Expressive for any new project β€” if the choice is mine that is. The reason for that, others here have already expressed quite eloquently. However, for completeness, I'll clarify what I mean.

Zend\MVC has, as @weierophinney says, a set of preconceptions about how things are done, yet they're very broad and unspecific. It also has a number of pre-wired structures in place. This requires you to know a lot of what those things are β€” if you really want to use it optimally.

To quote Matthew:

The problem is that zend-mvc is anything but beginner friendly at this point. You're required to deep dive into the event manager, service manager, and module system essentially from the outset and these require more than a passing understanding of OOP and design patterns.

Zend\Expressive (I'm referring to apps based on the Expressive Skeleton Installer) on the other hand, comes with barely any of these assumptions and requirements. It provides a very minimalist structure, essentially a:

  • A DI container
  • Router
  • Error handler for development
  • Template engine (if you need one)

Given that, you can very quickly get up to speed with creating exactly the app that you need. I’d argue very strongly that this approach, in contrast to the Zend\Mvc approach, is the more flexible and accommodating.

What’s more, you can mix and match the types of applications that you create. Just need an API, great, you can do that quite quickly. Want an HTML-based front-end, that’s available too. You can make use of any other Zend library, or non-Zend library. The buffet of choice and the flexibility provided is tantalising.

Given that, I’m not saying that Zend\Mvc is bad! What I am saying is that:

  • The learning curve is significantly lower with Zend\Expressive
  • The ways in which you can create applications, whether through multiple pieces of middleware, or by combining multiple Expressive apps, into one larger one, is a much more effective way to work
@weierophinney
Member

@settermjd I like how succinctly you've written this; would you be willing to write it up to include in both the zend-expressive and zend-mvc documentation, by any chance?

@settermjd
Contributor

Sure can. It'll be done by week's end.

@settermjd settermjd added a commit to settermjd/zend-expressive that referenced this issue Jan 13, 2017
@settermjd settermjd Add a section on when to choose Zend\Expressive over Zend\Mvc
After participating in the discussion on [When to choose Zend\Expressive
over
Zend\Mvc](zendframework#283),
issue #283, @weierophinney asked me to take what I'd written and add it
to the Zend\Expressive and Zend\Mvc docs. This commit adds said revised
version of my feedback, to the docs.
c054537
@albrin
albrin commented Feb 6, 2017

Zend Expressive designed it not to be able to use in a project different frameworks and/or libraries that support the standard PSR-7?

For example, routes requests "example.com/api/v1" or "/gallery" I want to treated Zend components, but routes "example.com/newscompany" Symfony or Phalcon.

I also think that Zend Expressive is well suited for Microservice Architecture?

Zend Expressive (PSR-7) is there any way to stop the dispute is best to choose a framework for development and focus on the development (code) programming PHP language.

@Ocramius
Member
Ocramius commented Feb 7, 2017

Zend Expressive designed it not to be able to use in a project different frameworks and/or libraries that support the standard PSR-7?

Expressive is a framework. It's the middleware that is/are designed to be reused. Interestingly, a Zend\Expressive\Application is also a middleware, so it can be chained.

For example, routes requests "example.com/api/v1" or "/gallery" I want to treated Zend components, but routes "example.com/newscompany" Symfony or Phalcon.

TBH, I'd rather set that up at gateway level, but yeah, it can be done with expressive too.

I also think that Zend Expressive is well suited for Microservice Architecture?

The word "microservice" is inflated. You can build a microservice even with the largest J2EE installation: the point is keeping the API surface minimal. The code amount is not really important.

Zend Expressive (PSR-7) is there any way to stop the dispute is best to choose a framework for development and focus on the development (code) programming PHP language.

This is a never-ending discussion, and TBH, it's good to keep having it. Without this sort of discussion, framework development would stall, and we would be back in the age of "one framework for everything", which has both advantages and disadvantages.

@albrin
albrin commented Feb 7, 2017 edited

@Ocramius Thank you for your comments.

TBH, I'd rather set that up at gateway level, but yeah, it can be done with expressive too.

Can any example, please? I do not quite understand what it was about.

@Ocramius
Member
Ocramius commented Feb 7, 2017

Can any example, please?

Before hitting PHP. Can be done in a load balancer or equivalent.

@weierophinney weierophinney added a commit that closed this issue Feb 9, 2017
@weierophinney weierophinney Merge branch 'hotfix/423'
Close #423
Fixes #283
90e62e6
@WebDeveloper4Life
WebDeveloper4Life commented Feb 20, 2017 edited

About what @xtreamwayz said

But if you share the zf2 and expressive config in the same container you can probably access those anyway without moving them into a Domain module (disclaimer: I haven't tried this yet).

I tried to do that with apigility in expressive but the major problem is that apigility use and injects in ResourceFactory another instance of ServiceManager than the container used by Expressive and probably the same will happen in Zend-MVC . Also Expressive allow you to add services using 'dependencies' key and in Zend-MVC 'service_manager' key .

I thought that one possibility is to merge both configs once in Expressive and once in Apigility when the general config is created but I don't really like to have to load and use two containers so how can I tell in Apigility(Zend-MVC) to use as container( $services ) the Expressive container?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment