Implementing a Front-End Architecture using Vue.js and Spryker

Ana Margarida Picoito
t14g
Published in
12 min readApr 26, 2023

--

Midjourney / prompt: code, vue.js, software development, javascript, abstract, masterpiece — ar 16:9 — v5

The Power Of “Just Doing It”

In the day-to-day life of a software developer, there are projects and then there are projects — the ones that present you with an immense technical challenge, as well as an enormous opportunity to grow and really come into your own. Recently, I had the chance to play an integral part in one of our commerce platform projects using Spryker: creating the front-end architecture from scratch using Vue.js framework as a base. Here’s my story and a technical walkthrough on how I tackled the challenge.

Not long ago, shortly after joining a new project at Turbine Kreuzberg, I was presented with a new challenge. I knew right away that it wasn’t going to be easy, but I also knew this was an opportunity for me to grow beyond my limitations and build something I could be proud of.

Here’s the situation: the project I was joining didn’t have a front-end architecture defined yet. That was a new starting point for me. All the projects I had worked on previously, already had most of the structure defined. I was there to help maintain and improve. This was different.

I was in a position where I had to propose a good technical solution.

I struggled many times with my choices, second guessing and playing devil’s advocate, but I wasn’t scared to fail and try new approaches. My team trusted me, which was extremely important for me in order to feel like I could fail and not be pressured. In the end, everything worked out — and, I dare say, better than first predicted.

Two options: now choose!

Let’s start with my “sink or swim” decision. Basically, I was presented with two options to start building the front-end.

  • Option 1: The first involved using Twig, a template engine for PHP (check out the documentation for details), combine it with Javascript/Typescript, and then apply the old DOM (Document Object Model) manipulation.
  • Option 2: The second option meant implementing Vue.js into Spryker, a framework that I was already familiar and felt really comfortable with.

To cut the suspense: I chose the latter. I was going to stay engaged with Vue.js.

Each project has its own unique set of requirements, making it important to find solutions that are tailored to specific needs. What works well for one project may not be the best approach for another. Taking these factors into consideration, the chosen approach was to implement the project in a way that best suited its specific requirements.

I chose to go with Vue.js for a variety of reasons, but the most important factor was that this project required a fast delivery of front-end components. Therefore, I chose to have a slower start, so later I could develop much faster. The “delivery curve” was much steeper in this scenario.

After discussing with my team’s product owner and getting acceptance for my proposed approach, the “only” matter left was actually implementing Vue.js into Spryker. And at first, I had no idea where to begin.

Research — and then: a breakthrough!

So, what to do? Research, of course. I started researching on the implementation of Vue.js into Spryker and tried to gather all the information I could find. I was confident that the Spryker modules would remain functional, as the team required a complete interface that worked seamlessly. To ensure that we could deliver the project within the given time frame, we planned to gradually replace these modules with Vue components one by one. What surprised me was the lack of available information to help me in my endeavor, neither in Spryker’s official documentation nor in our internal documentation.

After days of research, the feeling of being lost frightened me a bit. In times like these, I always try to apply the “mimicking” method, i.e. applying the same steps I’ve seen done before in other implementations — the “do what you know” approach, essentially. I realized my previous project had a suitable structure to use as a basis.

My first key goal was to simply make Vue.js work. The first step was to display something that was rendered from a Vue component on the website. As mentioned before, it was very important that whatever strategy or implementation I found, it would still allow us to see the current website with all its Spryker modules. To be able to do this, our app would have to be “hybrid”, meaning it would need to accept the usage of both Spryker modules and Vue.

Still, I needed more information, so I threw myself into our internal front-end developer Slack channel and asked around for some suggestions on how to start doing it. Finally, after some help from my colleagues, I had a road map of the implementation I could follow, yet was still lacking some pretty important information on how to implement Vue.js into an existing software repository. Luckily, one of our trusted technical directors, Özkan Yilmaz, guided me towards some useful Spryker documentation on adding front-end frameworks to a demo shop. Unfortunately, this illustration was made by using React, so with the patience and kindness of Özkan, we had a deep look into it and discussed how it would be applied to other front-end frameworks. (If you are curious, here’s the documentation: Integrating React into Atomic Frontend | Spryker Documentation.) With this guide, it was going to be really quick and easy for me to achieve my first goal.

How to implement a Vue.js front-end framework into Spryker

Alright, enough with the pleasantries. Let’s take a technical deep dive into what I did, step by step. In the documentation, Spryker implements React with atomic architecture (for more information: Atomic Frontend — general overview | Spryker Documentation), so I took the suggested approach and tried it with Vue.

Setup

Here are the dependencies that need to be installed first:

Necessary dependencies to install

Webpack is a module bundler in general terms which is used to bundle JavaScript codelines and other assets (such as CSS stylesheets and images) into a single file or a set of files that can be loaded by a web browser. It powers the core frontend functionality of Spryker. It might be hard to configure it sometimes but if it’s not done, Vue.js would not work.

So, to use vue-loader with webpack, import this first:

In JavaScript, the resolve object is a part of a Webpack configuration. It contains options that specify how Webpack should resolve modules and other dependencies when bundling the code.

This is what it looks like:

In this resolve, the vue alias is being used to reference the ‘vue/dist/vue.esm-bundler.js’ file, which is the main file for the Vue.js framework. This allows you to import Vue.js components using the vue alias instead of the full import path, which can make your code more concise and easier to read.

vue.esm-bundler.js allows us to still use runtime template compilation while using Wedback as a bundler.

One thing that took me long to understand was that the order of the file extensions matter. These options can help Webpack find and include the necessary files in the final bundle.

To configure Webpack to use vue-loader for all .vue files, add the following code to the module’s rules array (which has vue loaders in it) before anything else:

Inside the plugins array, create an instance:

Finally, your plugins section should look similar to this:

The DefinePlugin is a Webpack plugin which allows you to define global constants that can be used in your code.

The last two lines (VUE_OPTIONS_API and VUE_PROD_DEVTOOLS) are necessary to prevent console errors and warnings from appearing in the browser’s console devTools due to “missing config”.

Usage

When it comes to application of the code, Spryker recommends creating modules within the ShopUI framework and registering them to the index.ts file. This approach involves generating new instances of the component each time it is required.

Something like this:

We need to put vue-product-details in a Twig file because it is used to render the product details page of the e-commerce website and the Vue component is responsible for dynamically displaying the product details like name, price, images, etc.

This way, Spryker continuously creates a new Vue App instance for every created component.

In my opinion, this is not the ideal solution. Along with the fact that this is not the best performance wise, some inter-component communication necessities would’ve been lost, the state management can become more complex, translation problems among other things in the long-run.

Whenever a new Vue component is created, this solution uses the same code. By doing that, it causes code duplication which is also not an ideal solution.In this case, I thought of using a singleton design pattern to get rid of the recurring problems. I decided not to follow this approach blindly and started studying other ideas.

In my previous project, all the front-end and Vue framework was detached from the Spryker modules directory. We were using a front-end root folder and keeping every front-end related thing inside that. It had every potential to be an answer to my questions, but even though I wanted to implement this model, I was lacking time. All this implementation, trial-and-error discovery journey had already been highly time-consuming for me, especially during the sprints in which we, of course, should be presenting results to the client. Since it was an MVP project, it was important for stakeholders to see some progress. So, what to do?

I thought about applying a hybrid solution, kind of like a mixture of the approach in my previous project, but in a Spryker way, to keep some modularity.

I created a global wrapper root Vue component. This component enclosed the entire app, Spryker modules and all in order to avoid having to instantiate Vue components everytime they were used. By doing so, all components are initialized during the creation of the RootComponent. With this way, I could have both Spryker modules and Vue components inside this wrapper.

So I created a global object and registered all Vue components that are used directly on the twig files.

In export, register the shared components with its properties. This way, it makes it easy to import and use multiple related components in other parts of the application with a single import statement.

And then, they can be loaded by doing the following:

We create all components at once on the base level in the vendor.ts file.

The second import statement makes the sharedComponents object available to the current file, so that the components in it can be used in the application from anywhere.

The third import statement is providing a global icon component to the application.

The TjIcon component can be used from anywhere without having to import it. It’s globally available.

If you create a TjButton component, unless you’re using it in a twig file, you don’t actually need to add it to sharedComponents.

This is how components are registered and created:

Let’s take a deeper look into this piece of code. What is this “tj-”/”Tj” prefix actually?

After compiling, everything seemed to be working. I achieved the goal of seeing the component’s content on the website without ruining anything else. Everything was going well until I opened the browser console to check for errors.

And… BOOM!

There were a bunch of warnings! Oh, but warnings are not a big deal. They were to me. I had to find a solution because I really wouldn’t leave it like that. The errors and warnings on the browser’s developer tools console mentioned some components which were not properly registered in the application. Vue.js was unable to resolve Spryker modules on the Document Object Model (DOM).

Spryker modules look like custom tag elements on the DOM and Vue doesn’t know what they are. They can look like <toggler-accordion />. Since everything is now called inside the vue-app wrapper, Vue sees this and checks if it’s an HTML tag. If not, tries to resolve them as Vue components. This tag is neither of them, so it threw the error.

To resolve this, it was necessary to register these Spryker module tags as costume elements. According to Vue documentation, you can register a custom element by doing the following:

According to this, what should be done is prefix every Spryker module tag with something like <spryker-toggler-accordion> and then returning tag.startsWith(‘spryker-’) on the method above, but it would be way too many changes, so we can do it the other way around.

We prefix every Vue component with Tj and register all elements that don’t start with this prefix as custom.

After this, there were no more errors or warnings on the console.

Testing

This basic functionality worked very well until the merging process. I was getting a red pipeline because of failing tests. To figure out why, I looked at the screenshots (.png) that are taken every time there’s a failed Cest test, but all of them were completely blank.

First, I thought maybe there was a missing configuration but after a bit of moving things around and debugging, it looked like the problem was originating from the Vue wrapper. The contents inside a Vue component were not getting rendered. I didn’t feel comfortable checking it out all alone, so I asked Özkan’s help one more time. He said he didn’t experience something like that in his Spryker shop and had no idea why it was happening in ours. There was a lot of searching and a lot of time spent on this problem.

On the internet forums, there were people commenting about the same issue, rendering the Javascript of their webpages. In some posts, there were a few mentions about webdriver problems, so I checked Spryker’s documentation to see which webdriver was suggested (see: Running tests with the Docker SDK | Spryker Documentation). Chromedriver was the default webdriver shipped with Docker SDK but for some reason it was not working. We also tried using other web drivers to no luck. So, we decided to ask for help one last time: Jorge Murta, another Technical Director at Turbine Kreuzberg, to the rescue!

It turned out that our Spryker shop instance didn’t have a configured Chromedriver, not by default as we initially thought. Codeception was fully configured to run with Chromedriver but the service itself was not configured, so we did as described in the documentation. I found it really strange that Spryker uses Chromedriver as default but without any pre-configuration. It was very time-consuming for us to solve this problem and especially frustrating since the solution was already in front of us.

So, what happened after the configuration?

Green Pipeline! 🟢

Conclusion

So, this was my approach to this challenge and how I implemented a Vue.js-based architecture into a Spryker demoshop.

It’s very important to underline the fact that this was “my” solution. One project is not like the other and our maturity and knowledge is constantly growing. I’ve already started questioning some of the choices I made and documented. Was there a better way to do some parts?

Most likely there was, but I did the best I could with the knowledge I had. And overall, I’m still convinved of our implementation.

Even if you think you lack the experience to solve a challenge you’ve never had to tackle before, “just doing it” will go a long way. Of course, always be aware of what you don’t know and never hesitate to ask others to help fill the gaps. In the end, I didn’t just gain an immense amount of technical knowledge, but also learned something about myself.

What we develop today will always have the potential to be better tomorrow. We just need to stop being scared, overthinking and just try it. This process has convinced me of the usefulness of taking an experimental approach. Thank you to my team and everyone who trusted and helped me along the way.

--

--