Nabadi Barbershop
ბარბერშოპის მთელი საქმიანობა, ქართულად და ინგლისურად. მომხმარებელი კონკრეტულ ბარბერთან კონკრეტულ სერვისზე იწერება და ხედავს მხოლოდ იმ დროს, როცა ამ ბარბერს რეალურად შეუძლია მუშაობა. მის უკან პერსონალის კონსოლი მართავს ბარბერშოპს: სავარძლების დღიური განრიგი, ჯავშნის გარეშე მოსული მომხმარებლები, სერვისების კატალოგი, თითოეული ბარბერის სამუშაო საათები და დასვენების დრო, აქციები, შეფასებების მოდერაცია, ანალიტიკა და უფლებების მატრიცა, რომელიც წყვეტს, ოთხი როლიდან რომელი რას ხედავს. ყველაფერი თავიდან ბოლომდე ჩვენ ავაგეთ — Django 5 + DRF Postgres-ზე და მასზე ორი React 19 ფრონტენდი.
წელი: 2026 · კატეგორია: დაჯავშნა
ჩვენი როლი
Full-stack. მონაცემთა მოდელი, ხელმისაწვდომობის ძრავა და ჯავშნების შეზღუდვები, უფლებების სისტემა და ორივე ფრონტენდი.
პრობლემა
ბარბერშოპის დღიური დახლის უკან დადებული ქაღალდის წიგნია და მხოლოდ მაშინ მუშაობს, როცა მის გვერდით ვინმე დგას. მომხმარებელმა, რომელსაც ხუთშაბათს ექვს საათზე წვერის მოდელირება კონკრეტულ ბარბერთან უნდა, უნდა დარეკოს, დაელოდოს, სანამ საჭირო გვერდს იპოვიან, და რეცეფციონისტის სიტყვას ენდოს. ბარბერშოპმა კი არ იცის, როგორი იქნება მომავალი კვირა, სანამ ის არ დადგება.
ყველა ჩავარდნა სინამდვილეში ერთი და იგივე ჩავარდნაა: დღიური ერთადერთი ეგზემპლარია ერთ ადგილას. ორ ადამიანს ერთსა და იმავე სავარძელს ჰპირდებიან. ბარბერის დასვენების დღე პერსონალიდან ერთს ახსოვს და მეორეს — არა. მუდმივი მომხმარებლის ისტორია — რა გაიკეთა ბოლოს, რომელ ბარბერთან, მოვიდა თუ არა — მხოლოდ ვიღაცის მეხსიერებაში არსებობს.
მიდგომა
ჯავშნის წესები იმ სისტემას ეკუთვნის, რომელიც ჯავშნებს ინახავს, ამიტომ სწორედ იქ განვათავსეთ.
დრო მხოლოდ მაშინ შემოთავაზდება, თუ ბარბერი ამ დროს მუშაობს, დასვენებაზე არ არის, უკვე დაკავებული არ არის და სერვისი დახურვამდე ეტევა. ამ ერთ კითხვას ხელმისაწვდომობის ერთი ძრავა პასუხობს, და მას ეკითხება მომხმარებლის კალენდარიც, რეცეფციის ეკრანი ჯავშნის გარეშე მოსულთათვისაც და ადმინის დღის ხედიც — ამიტომ ორი ეკრანი ვერასოდეს იდავებს, თავისუფალია თუ არა სავარძელი. ქვემოთ კი Postgres-ის გამორიცხვის შეზღუდვა გადაფარვას ფიზიკურად შეუძლებელს ხდის და არა უბრალოდ ნაკლებად სავარაუდოს.
ამის გარშემო ბარბერშოპის დანარჩენი ნაწილია: სერვისების კატალოგი ბარბერის მიხედვით ფასისა და ხანგრძლივობის ჩანაცვლებით, სამუშაო საათები, რომლებიც საჭიროებისას ბარბერშოპის საათებზე გადადის, დასვენების დრო, რომელიც შეიძლება ერთი ბარბერის ან ყველასი იყოს, პრომო-კოდები გამოყენების ლიმიტით, შეფასებები, რომელთა დატოვებაც მომხმარებელს მხოლოდ რეალურად შედგარ ვიზიტზე შეუძლია, და ამ ყველაფერზე — აუდიტის ჟურნალი.
მთავარი მომენტები
- ორმაგ ჯავშანს ბაზა უარყოფს და არა აპლიკაცია: Postgres-ის გამორიცხვის შეზღუდვა (barber, time range)-ზე ნიშნავს, რომ ერთსა და იმავე სავარძელზე ერთდროულად მოსული ორი მოთხოვნიდან ორივე ვერ გაიმარჯვებს, API როგორც არ უნდა გამოიძახონ.
- ხელმისაწვდომობა გამოითვლება და არ ინახება. ბარბერის საკუთარი საათების არარსებობისას ბარბერშოპის საათები მოქმედებს, დასვენების დრო აკლდება — შესვენებაც ასევე შეიყვანება, ამიტომ სინქრონში შესანარჩუნებელი მეორე ცნება არ არსებობს — არჩეული სერვისის ხანგრძლივობა წყვეტს, რამდენად ფართო უნდა იყოს დრო, ხოლო ჯავშნამდე მინიმალური ვადა დღევანდელი დღის დასაწყისს ჭრის, ასე რომ, კალენდარი მხოლოდ იმ დროს სთავაზობს, რომლის შესრულებაც ბარბერშოპს შეუძლია.
- ოთხი როლი და თექვსმეტგასაღებიანი უფლებების ერთი ცხრილი. ყოველი ფლაგი სამჯერ მოწმდება — DRF permission კლასით ენდფოინთზე, მარშრუტზე React-ის წვდომის შემოწმებით და გვერდითი მენიუთი, რომელიც წყვეტს, ღირს თუ არა ეკრანის ჩვენება. ბექენდის ტესტი საწყის მატრიცას, აღსრულების შემოწმებებსა და გასაღებების სიას ერთმანეთთან აბამს; კონსოლის საკუთარ ასლს კი სამივეზე გაშვებული პარიტეტის შემმოწმებელი ინარჩუნებს სინქრონში.
- პერსონალის ყოველი ცვლილება აუდიტის ჩანაწერს წერს სრული „მანამდე/შემდეგ“ სხვაობით, წაშლამდე გადაღებული სნეპშოტის ჩათვლით, ამიტომ კითხვას „ვინ შეცვალა ეს და როდის“ პასუხი აქვს მაშინაც, როცა სტრიქონი აღარ არსებობს.
- ორენოვანი თვით კონტენტის მოდელამდე: სერვისები, კატეგორიები და ლენდინგის ყველა სექცია ქართულსა და ინგლისურს გვერდიგვერდ ინახავს, და ადმინი ორივეს ერთ ფორმაში არედაქტირებს.
- საჯარო საიტს ადმინი არედაქტირებს — ჰირო-ბლოკი, გალერეა, სამუშაო საათები, მისამართი, სოციალური ბმულები და რჩეული შეფასებების ზოლი მონაცემებია და არა დეპლოი.
- 564 ბექენდ-ტესტი და 36 ფრონტენდ-სუიტა 180 შემთხვევით, სადაც ყველაზე სრული დაფარვა ჯავშნის შეზღუდვებსა და დროის შერჩევის ალგორითმზე მოდის — CI-ში 78%-იანი დაფარვის ზღვრით.
არქიტექტურა
- მომხმარებლის საიტი: ოთხნაბიჯიანი ჯავშნის ვიზარდი — სერვისი, ბარბერი, დღე, დრო — რომელიც მდგომარეობას Zustand-ში ინახავს, ასე რომ, გვერდის განახლება ან უკან დაბრუნების ღილაკი ნახევრად გაკეთებულ ჯავშანს არ კარგავს. ლენდინგი, გალერეა, სამუშაო საათები და საკონტაქტო ინფორმაცია CMS-იდან მიეწოდება, ამიტომ ბარბერშოპი საკუთარ მთავარ გვერდს თავად არედაქტირებს.
- პერსონალის კონსოლი: ბარბერშოპის სამუშაო სივრცე: დღის კალენდარი თითო ზოლით თითო ბარბერზე, ჯავშნების სია ფილტრებითა და XLSX ექსპორტით, ჯავშნის გარეშე მოსულთა რეგისტრაცია რეცეფციაზე, მომხმარებელთა ისტორიები პერსონალის შიდა შენიშვნებით, შეფასებების მოდერაცია, ანალიტიკა და თავად უფლებების მატრიცა.
- ხელმისაწვდომობის ძრავა: ერთი მოდული პასუხობს კითხვას „როდის შეუძლია ამ ბარბერს ეს სერვისი“. ბარბერის სამუშაო საათები ბარბერშოპის საათებზე გადასვლით, ბარბერის საკუთარი და მთელი ბარბერშოპის დასვენების დრო (რითაც შესვენებაც შეიყვანება), სერვისის ხანგრძლივობა და ბარბერის ჩანაცვლებები, ჯავშნამდე მინიმალური ვადა და ჯავშნის მაქსიმალური ჰორიზონტი — ყველაფერი Asia/Tbilisi-ში ითვლება, ერთადერთ დროის სარტყელში, რომელიც ბარბერშოპს ოდესმე აინტერესებდა.
- უფლებები: ოთხი როლი — ადმინი, ადმინისტრატორი, ბარბერი, მომხმარებელი — საწყისი მონაცემებით შევსებულ ფუნქციის ფლაგების ცხრილზე. ბექენდი მას DRF permission კლასით ამოწმებს, კონსოლი მარშრუტს ბლოკავს და ნავიგაციის პუნქტს მალავს, ხოლო სამივეზე გაშვებული პარიტეტის შემმოწმებელი მათ სინქრონში ინარჩუნებს — რადგან უფლება, რომელიც სამიდან მხოლოდ ორ ადგილას მოწმდება, ღრმა ბმულის მომლოდინე ბაგია.
- ბექენდი: თერთმეტი Django აპლიკაცია, 19 მოდელი, DRF ცალსახა per-view უფლებებით, JWT HttpOnly ქუქიებში როტაციითა და შავი სიით, შეცდომის პასუხის ერთიანი ფორმატი, რომელსაც ყველა ენდფოინთი იყენებს, და Celery შეხსენების SMS-ებისა და იმ გასუფთავებისთვის, რომელიც გუშინდელ დაუსრულებელ ვიზიტებს ხურავს.
- კონტენტი და იდენტობა: ლენდინგის კონტენტი და საიტის პარამეტრები სინგლტონებია წყვილი ქართული/ინგლისური სვეტებით; ჯავშნის პოლიტიკა — დროის ბიჯი, ჯავშნამდე მინიმალური ვადა, რამდენად შორს შეიძლება ჩაწერა, გაუქმების ფანჯარა — ადმინის მიერ რედაქტირებადი კონფიგურაციაა და არა კოდში ჩაკომპილებული მუდმივები.
ციფრებში
Django მოდელები: 19 · ბექენდ-ტესტები: 564 · ფრონტენდის კოდის ხაზები: 22k · ენები: 2
შედეგები
- სავარძელი, რომელსაც მომხმარებელს სთავაზობენ, ისაა, რომლის დაპირების შესრულებაც ბარბერშოპს რეალურად შეუძლია — ორმაგი ჯავშნის საწინააღმდეგო შეზღუდვა ბაზაშია, ამიტომ მას კოდის ვერცერთი გზა ვერ აუვლის გვერდს.
- ბარბერშოპის დღიური აღარ არის ერთადერთი ეგზემპლარი ერთ ადგილას: რეცეფცია, ბარბერი და მომხმარებელი ერთსა და იმავე დღეს კითხულობენ.
- სამუშაო საათები, ფასები, სერვისები, აქციები და მთელი მთავარი გვერდი ადმინის ქმედებები გახდა და არა დეპლოი.
- პერსონალის ყოველი ცვლილება ჯავშანში, მომხმარებლის მონაცემებში თუ ფასში აუდიტის ჩანაწერიდან აღდგენადია, მათ შორის ისეთებიც, რომლებმაც რამე წაშალა.
რა გამოვიყენეთ
- ბექენდი: Python 3.13, Django 5.2 LTS, Django REST Framework 3.15, SimpleJWT 5.3 (httpOnly ქუქიები, როტაცია + შავი სია), drf-spectacular 0.28, django-cors-headers 4.6, phonenumbers 8.13
- მონაცემები და ფონური ამოცანები: PostgreSQL 16 + btree_gist, psycopg 3.2, Redis 7, Celery 5.4, django-celery-beat 2.9, openpyxl 3.1, Pillow 11
- ფრონტენდები: React 19, TypeScript 6, Vite 8, Tailwind CSS 4, TanStack Query 5, Zustand 5 (მომხმარებლის აპლიკაცია), Recharts 3 (კონსოლი), Radix UI, React Hook Form 7 + Zod 3, React Router 7, i18next 23 (KA/EN), date-fns 4
- ტესტირება და ხარისხი: pytest 8.3 + pytest-django 4.9, pytest-cov (დაფარვის ზღვარი: 78%), factory-boy 3.3, Vitest 2 + Testing Library, ruff 0.8 + black 24.10, ESLint 10 + Prettier 3, pre-commit, GitHub Actions
- გაშვება და მიწოდება: Docker Compose, Gunicorn 23, nginx, WhiteNoise 6.8, Sentry, smsoffice.ge SMS გეითვეი, just
დემო
ორივე ფრონტენდი, რომელთა Django ბექენდიც ბრაუზერშივე მომუშავე სიმულირებული ვერსიით არის ჩანაცვლებული: საწყისი მონაცემები ბანდლშია ჩაშენებული და მეხსიერებიდან მიეწოდება. ორი აპლიკაცია ერთ ბანდლად მიეწოდება და ერთსა და იმავე საცავს იყენებს, ამიტომ საიტზე გაკეთებული ჯავშანი კონსოლზე გადასვლისას უკვე იქ გელოდებათ — კუთხეში მდგარი ბანერი შეგიყვანთ როგორც მომხმარებელს ან როგორც პერსონალს. საწყისი სტრიქონები საცავში არასოდეს იწერება, ამიტომ გვერდის გადატვირთვა ხელუხლებელ ბარბერშოპს აბრუნებს.