[{"data":1,"prerenderedAt":430},["ShallowReactive",2],{"project-case-brendmart":3},{"blocks":4,"person":372},[5,15,50,113,129,146,164,303,335,359,365],{"component":6,"data":7},"ProjectCaseHeader",{"index":8,"subtitle":9,"name":10,"headline":11,"description":12,"url":13,"tone":14},"03","ONLINE STORE · DELIVERY FROM EUROPE","Brendmart","600+ orders a month by the time of handover","An online store of clothing, shoes and accessories by global brands from European stores, with delivery to Russia, Belarus and Kazakhstan.","https:\u002F\u002Fbrendmart.com\u002F","secondary",{"component":16,"data":17},"ProjectCaseCover",{"image":18,"imageMobile":36,"imageAlt":48,"frame":49},{"src":19,"width":20,"height":21,"variants":22},"\u002F_nuxt\u002Fpreview.DQ_tOI1d.webp",1440,900,[23,26,29,32,35],{"src":24,"width":25},"\u002F_nuxt\u002Fpreview.BoDLKM3K.webp",480,{"src":27,"width":28},"\u002F_nuxt\u002Fpreview.nOXN3WDE.webp",720,{"src":30,"width":31},"\u002F_nuxt\u002Fpreview.Ybg35rJg.webp",960,{"src":33,"width":34},"\u002F_nuxt\u002Fpreview.DvOG594N.webp",1200,{"src":19,"width":20},{"src":37,"width":38,"height":39,"variants":40},"\u002F_nuxt\u002Fpreview-mobile.BDHhqLCn.webp",600,1298,[41,44,47],{"src":42,"width":43},"\u002F_nuxt\u002Fpreview-mobile.CSOton1R.webp",160,{"src":45,"width":46},"\u002F_nuxt\u002Fpreview-mobile.YlCbSuVE.webp",300,{"src":37,"width":38},"Home page of the Brendmart website","devices",{"component":51,"data":52},"ProjectCaseMeta",{"tags":53,"stack":57},[54,55,56],"E-commerce","Full-Stack","SEO",[58,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111],{"label":59,"own":60},"Nuxt 4",true,{"label":62,"own":60},"Vue 3",{"label":64,"own":60},"TypeScript",{"label":66,"own":60},"Pinia",{"label":68,"own":60},"Nuxt i18n",{"label":70,"own":60},"Nuxt Icon",{"label":72,"own":60},"zod",{"label":74,"own":60},"VueUse",{"label":76,"own":60},"Tailwind CSS",{"label":78,"own":60},"daisyUI",{"label":80,"own":60},"Vitest",{"label":82,"own":60},"Laravel",{"label":84},"Go",{"label":86,"own":60},"PostgreSQL",{"label":88,"own":60},"Redis",{"label":90,"own":60},"RabbitMQ",{"label":92,"own":60},"S3 \u002F MinIO",{"label":94},"ClickHouse",{"label":96},"Elasticsearch",{"label":98,"own":60},"Docker",{"label":100},"Traefik",{"label":102},"Kubernetes",{"label":104},"Argo CD",{"label":106},"GitLab CI",{"label":108,"own":60},"ELK",{"label":110,"own":60},"Grafana",{"label":112},"Prometheus",{"component":114,"data":115},"ProjectCaseFacts",{"facts":116},[117,120,123,126],{"key":118,"value":119},"role","Fullstack Developer",{"key":121,"value":122},"team","2 fullstack developers and a junior frontend developer",{"key":124,"value":125},"duration","January 2025 – March 2026, launched in May 2025",{"key":127,"value":128},"status","Handed over, the store is live",{"component":130,"data":131},"ProjectCaseRole",{"id":118,"name":132,"items":133},"What I did",[134,137,140,143],{"title":135,"text":136},"Design and UI kit","The design is built around the product: the interface does not compete with the photos of brands from mass market to luxury, and the catalog, product page and cart work the same way so customers quickly find their way around the store. The UI kit is built on daisyUI: components take their colors and shapes from the theme, so the look changes by switching the theme rather than redoing the layout.",{"title":138,"text":139},"Nuxt storefront","Proposed and introduced Nuxt: the storefront runs as a separate server-rendered app rather than part of a monolith. For a store with a catalog of more than 2 million products, this means sales from search: search engines get a ready page with a description and markup and index the whole catalog. And a separate app evolves without waiting for backend releases.",{"title":141,"text":142},"Admin panel and API","The client side of the operator's admin panel, the layer of backend requests for the admin panel and the storefront, Laravel API endpoints for the storefront.",{"title":144,"text":145},"Review","Reviewed the junior frontend developer's merge requests. Reviews kept the code in one style and caught bugs before the test stand, and through them the junior developer picked up the project's practices faster.",{"component":147,"data":148},"ProjectCaseChapter",{"id":149,"name":150,"blocks":151},"context","Context",[152],{"component":147,"data":153},{"id":154,"name":155,"blocks":156},"business","Business",[157,161],{"component":158,"data":159},"ProjectCaseText",{"text":160},"Brendmart sells clothing, shoes and accessories by global brands from European stores, luxury and mass market: Prada, Brunello Cucinelli, Max Mara, Etro, Swarovski. Its customers live in Russia, Belarus and Kazakhstan.",{"component":158,"data":162},{"text":163},"The client buys the item from a foreign seller on the customer's behalf and delivers it to the EAEU countries. The store launched in May 2025.",{"component":147,"data":165},{"id":166,"name":167,"blocks":168},"process","Process, architecture and decisions",[169,190,222,233,244,262,270],{"component":147,"data":170},{"id":171,"name":172,"blocks":173},"flow","How people buy",[174],{"component":175,"data":176},"ProjectCaseSteps",{"steps":177},[178,181,184,187],{"title":179,"text":180},"Catalog","The price already includes delivery and customs duties. In development: server rendering of a catalog of 2M+ products, a cached category tree",{"title":182,"text":183},"Concierge","If an item is not in the catalog, the customer can leave a purchase request. In development: a request form with zod validation",{"title":185,"text":186},"Cart","The cart and favorites live in a guest session, no sign-up needed. In development: a guest session through a proxy in a signed cookie, cart and favorites state in Pinia",{"title":188,"text":189},"Delivery","The item is bought from a seller in Europe, express delivery from 5 days. In development: the order goes to the admin panel, where an operator buys the item and handles the delivery",{"component":147,"data":191},{"id":192,"name":193,"blocks":194},"architecture","Architecture",[195],{"component":196,"data":197},"ProjectCaseArchitecture",{"items":198},[199,202,206,214,218],{"label":200,"title":138,"text":201},"STOREFRONT","A separate server-rendered app on Nuxt 4, Pinia, Tailwind and daisyUI. Page data arrives in _payload.json. Nuxt was chosen for server rendering, which a large catalog needs for SEO. Pinia keeps the shared state of the cart and favorites, and Tailwind with daisyUI lets the look change through the theme.",{"label":203,"title":204,"text":205},"ADMIN","Operator admin panel","The private part of the store for staff. The operator receives orders and purchase requests, buys the item from the seller and handles the delivery. The panel is built on the same UI kit as the storefront, so new screens are made from ready components.",{"label":207,"title":208,"text":209,"code":210,"wide":60},"API","Public REST","Cart, favorites, a cached category tree. The guest session goes through a proxy and is kept in a signed cookie. The category tree is cached because it rarely changes but is needed on every page. The signed cookie keeps the cart without sign-up and prevents the session from being forged.",[211,212,213],"\u002Fapi\u002Fpublic\u002Fv1","\u002Fcached\u002Fcategories-tree","\u002Fproxy\u002Fsession\u002Fguest",{"label":215,"title":216,"text":217},"BACKEND","Services and data","Laravel and Go, queues on RabbitMQ. PostgreSQL, Redis, Elasticsearch, ClickHouse, S3 \u002F MinIO. Laravel speeds up business logic development, and Go handles the high-load services. RabbitMQ moves heavy tasks to the background, Elasticsearch searches 2 million products, and ClickHouse stores analytics.",{"label":219,"title":220,"text":221},"INFRA","Infrastructure","Docker, Traefik, Kubernetes and Argo CD, builds in GitLab CI. Logs in ELK, metrics in Grafana and Prometheus. Kubernetes and Argo CD update the store without downtime and roll back a release in one step. ELK, Grafana and Prometheus reveal errors and load before customers notice them.",{"component":147,"data":223},{"id":224,"name":225,"blocks":226},"server-driven","Pages are built from the API",[227,230],{"component":158,"data":228},{"text":229},"Changing the home page takes no release. The backend returns the page as a JSON array shaped like a tree, and the storefront builds the page from components according to it.",{"component":158,"data":231},{"text":232},"The home page is built this way, and any other page can be built the same way: content and structure change through the API.",{"component":147,"data":234},{"id":235,"name":236,"blocks":237},"storefront","Search, languages and accessibility",[238,241],{"component":158,"data":239},{"text":240},"Search engines get a ready page from the server, with its own title, description, Open Graph and schema.org WebSite and WebPage markup. The interface is translated into Russian, English and Spanish.",{"component":158,"data":242},{"text":243},"Accessibility is built into the storefront to the extent needed: semantic page landmarks, alt text on images, labels on buttons and links, zoom is not blocked. Semantics works for three sides at once. Customers with a screen reader or a keyboard can place an order. Search engines understand the page structure better and rank it more accurately. And developers find clear markup easier to maintain.",{"component":147,"data":245},{"id":246,"name":247,"blocks":248},"integrations","Integrations",[249,252],{"component":158,"data":250},{"text":251},"The integrations cover tasks the store does not need to build from scratch. Card payments take money right on the site. Jivo online chat lets customers quickly ask about a product or an order without leaving the page. Yandex Metrica shows where customers come from and at which step they leave.",{"component":253,"data":254},"ProjectCaseIntegrations",{"items":255},[256,258,260],{"label":257},"Card payments",{"label":259},"Jivo online chat",{"label":261},"Yandex Metrica",{"component":147,"data":263},{"id":264,"name":265,"blocks":266},"security","Data security",[267],{"component":158,"data":268},{"text":269},"For customs clearance the store keeps customers' personal data. Such fields are encrypted in the database with Laravel's built-in tools, and the key is stored apart from the code and the database. Access has two levels: customers and staff (operators and administrators).",{"component":147,"data":271},{"id":272,"name":273,"blocks":274},"quality","Quality and releases",[275,278,281,294,297,300],{"component":158,"data":276},{"text":277},"The project has three kinds of tests. Component tests in Vitest check the UI kit logic. Visual regression tests compare a block with a reference screenshot and catch accidental layout changes, especially when the theme changes. E2E tests in Playwright check that a customer can get from the catalog to an order.",{"component":158,"data":279},{"text":280},"Each task lives in its own branch and, once merged into develop, goes through CI checks and onto the test stand. On release day a preprod branch is assembled for acceptance in a production-like environment, then master updates the live server without downtime.",{"component":282,"data":283},"ProjectCasePipeline",{"stages":284},[285,288,291],{"name":286,"next":287},"develop","CI and test stand",{"name":289,"next":290},"preprod","final acceptance",{"name":292,"next":293},"master","zero-downtime release",{"component":158,"data":295},{"text":296},"Production errors are tracked continuously, and if something goes wrong the release is quickly rolled back to the previous stable version.",{"component":158,"data":298},{"text":299},"Sprints lasted two weeks. At the end of each one the client saw what was done, what was not and why, and together we planned the next two weeks.",{"component":158,"data":301},{"text":302},"This process made it possible to ship updates every two weeks without store downtime, and frequent style changes did not break the layout or the customer's path to an order.",{"component":147,"data":304},{"id":305,"name":306,"blocks":307},"challenges","Challenges",[308],{"component":309,"data":310},"ProjectCaseChallenges",{"items":311},[312,325],{"id":313,"name":314,"rows":315},"nuxt","A storefront apart from the monolith",[316,319,322],{"key":317,"text":318},"problem","A storefront inside a monolith is hard to develop separately from the backend and cannot be reused on other platforms.",{"key":320,"text":321},"solution","Proposed and introduced Nuxt. The storefront became a separate server-rendered app that takes its data from the backend's public API.",{"key":323,"text":324},"result","The storefront can grow without touching the backend, and mobile apps can be built from the same code by adding Capacitor.",{"id":326,"name":327,"rows":328},"themes","A new look in a day",[329,331,333],{"key":317,"text":330},"The look often had to change at the client's request, and drawing every variant in Figma takes a long time.",{"key":320,"text":332},"Built the styling on Tailwind and daisyUI so that the theme switches as a whole, with the store's look moved into its own daisyUI theme. A prototype and theme variants came quickly from GPT-4: we asked it to lay out a prototype and suggest several variants.",{"key":323,"text":334},"From the client's request to a page prototype in a new theme took no more than an hour. Rebuilding the whole project in a new style takes a day thanks to the daisyUI-based UI kit.",{"component":147,"data":336},{"id":337,"name":338,"blocks":339},"results","Metrics and results",[340,356],{"component":341,"data":342},"ProjectCaseResults",{"stats":343},[344,347,350,353],{"value":345,"label":346},"600+","orders a month at handover",{"value":348,"label":349},"2M+","products in the catalog",{"value":351,"label":352},"~4 mo.","from the start of development to launch",{"value":354,"label":355},"≤ 1 h","for a new theme prototype on the client's request",{"component":158,"data":357},{"text":358},"By handover the store was taking more than 600 orders a month from customers in Russia, Belarus and Kazakhstan.",{"component":360,"data":361},"ProjectCaseCta",{"id":362,"title":363,"text":364,"url":13},"cta","Need a store with a large catalog that is easy to restyle?","I will design the UI and the UI kit, build a server-rendered Nuxt storefront, the admin panel and the API.",{"component":366,"data":367},"ProjectCaseNext",{"slug":368,"name":369,"index":370,"tone":371},"tamriel-codex","Tamriel Codex","04","accent",{"name":373,"alternateNames":374,"role":119,"skills":377},"Michael Malakhov",[375,376],"Михаил Малахов","Mikhail Malakhov",[378,64,379,380,381,382,383,384,385,386,76,387,388,389,390,56,391,392,393,394,82,395,86,88,396,98,397,398,399,400,401,402,403,404,405,406,80,407,408,112,409,410,411,412,413,414,415,416,417,418,66,419,420,421,422,423,424,425,426,427,428,429],"JavaScript (ES6+)","HTML5","CSS3","Sass","Vue.js (Vue 2\u002FVue 3, Composition API)","Nuxt 3\u002F4","React","Next.js","Vue Router","Bootstrap","SPA","SSR","MPA","Chrome DevTools","Node.js","NestJS","Express","Rust","REST API","Nginx","CI\u002FCD","GitHub Actions","Vite","Webpack","Git","GitHub","GitLab","Figma","Redux","Jest","Tauri","Storybook","Jira","Claude Code","Codex","Gemini","MCP","Atlassian Rovo","Zustand","SWR","TanStack Query","Vuex","React Hook Form","Zod","Capacitor","GraphQL","WebSocket","Clinic.js","autocannon","Lighthouse","GitHub Copilot","AI agents",1793309471095]