Giới thiệu dự án

Sự phát triển mạnh mẽ của ngành dịch vụ ẩm thực (F&B) cùng xu hướng số hóa lối sống đã biến điện thoại thông minh thành công cụ thiết yếu để tìm kiếm trải nghiệm ăn uống. Theo các khảo sát thị trường tiêu dùng kỹ thuật số tại Việt Nam giai đoạn 2018–2019, hơn 70% giới trẻ và sinh viên tại các đô thị lớn như TP. Hồ Chí Minh có thói quen tra cứu thông tin quán ăn, thực đơn và giá cả qua thiết bị di động trước khi quyết định trải nghiệm. Tuy nhiên, người dùng thường xuyên đối mặt với các vấn đề: thông tin phân mảnh, đánh giá thiếu xác thực, giao diện ứng dụng cồng kềnh và tốc độ tải trang chậm do chèn quá nhiều quảng cáo thương mại.

Đề tài "Tìm hiểu và xây dựng ứng dụng tìm kiếm quán ăn trên hệ điều hành iOS" do sinh viên Huỳnh Thế Cương và Bùi Đỗ Trung Trực (Khoa Công nghệ Thông tin, Trường Đại học Sư phạm Kỹ thuật TP.HCM) thực hiện dưới sự hướng dẫn của TS. Nguyễn Thiên Bảo, tập trung giải quyết bài toán cốt lõi: Cung cấp một nền tảng tìm kiếm quán ăn nhanh chóng, trực quan, hỗ trợ cộng đồng chia sẻ địa điểm ẩm thực với cơ chế kiểm duyệt bài viết chặt chẽ trên nền tảng iOS.

+-----------------------------------------------------------------------+
|                         MỤC TIÊU DỰ ÁN                                |
+-----------------------------------------------------------------------+
| 1. Nghiên cứu chuyên sâu ngôn ngữ Swift và lập trình ứng dụng iOS     |
| 2. Xây dựng RESTful API hiệu năng cao với Node.js runtime             |
| 3. Thiết kế hệ thống cơ sở dữ liệu phi quan hệ (NoSQL) với MongoDB    |
| 4. Tích hợp Firebase Authentication & Firebase Cloud Messaging (FCM)  |
| 5. Hoàn thiện ứng dụng iOS Native kết hợp bản đồ định vị MapKit       |
+-----------------------------------------------------------------------+

Giải pháp tiếp cận kết hợp sức mạnh xử lý bất đồng bộ (non-blocking I/O) của Node.js ở tầng máy chủ, tính linh hoạt của MongoDB/Mongoose trong việc quản lý dữ liệu phi cấu trúc, cùng trải nghiệm mượt mà của giao diện iOS Native viết bằng Swift. Hệ thống đáp ứng các chỉ số kỹ thuật: thời gian phản hồi API dưới 150ms, hiển thị bản đồ định vị theo thời gian thực với độ trễ thấp và quy trình phân phối qua TestFlight.


Phân tích và thiết kế giải pháp

Phân tích hiện trạng

Khảo sát các nền tảng tìm kiếm địa điểm ẩm thực phổ biến tại Việt Nam cho thấy những khoảng trống công nghệ và trải nghiệm người dùng:

Tiêu chí Foody Lozi ClingMe Giải pháp đề xuất
Nền tảng chính Web, iOS, Android iOS, Android (Mạng xã hội ảnh) iOS, Android (Đa tiện ích) iOS Native (Swift)
Giao diện & Trải nghiệm Nhiều thông tin, dễ gây rối mắt Trọng tâm hình ảnh phong cách Instagram Trực quan, tích hợp tiện ích đô thị Tối giản, tập trung vào tìm kiếm và định vị
Cơ chế đóng góp nội dung Đánh giá cộng đồng tự do Người dùng đăng bài tự do Tổng hợp tự động & đối tác Người dùng đăng bài qua kiểm duyệt Admin
Tốc độ tải dữ liệu Trung bình do tải nhiều quảng cáo Tương đối nhanh Ổn định Rất nhanh nhờ JSON RESTful API tinh gọn

Ma trận phân loại yêu cầu người dùng (MoSCoW)

  • Must have (Bắt buộc): Tìm kiếm theo tên quán/địa chỉ; Xem thông tin chi tiết (thực đơn, giá, giờ mở cửa); Tích hợp bản đồ và định vị; Đăng nhập/Đăng ký tài khoản; Lưu quán ăn yêu thích.
  • Should have (Nên có): Đăng bài chia sẻ địa điểm mới; Kiểm duyệt bài viết phía Admin; Bình luận tương tác; Báo cáo (Report) quán ăn sai thông tin.
  • Could have (Có thể có): Nhận thông báo đẩy (Push Notification) từ hệ thống; Thay đổi thông tin cá nhân (Avatar, Display Name, Password).
  • Won't have (Chưa thực hiện ở giai đoạn này): Đặt bàn trực tuyến (Reservation); Đặt món và thanh toán qua cổng điện tử; Mời bạn bè thời gian thực.

Thiết kế hệ thống

Hệ thống được thiết kế theo mô hình 3 tầng (3-Tier Architecture) kết hợp kiến trúc hướng dịch vụ micro-scale:

graph TD
    Client[iOS Client Application - Swift / MapKit / CocoaPods]
    APIServer[Backend Server - Node.js / Express REST API]
    AuthServer[Firebase Authentication & FCM Service]
    Database[(MongoDB Database / Mongoose ODM Engine)]

    Client -->|HTTPS / JSON Request| APIServer
    Client -->|Auth Token / Push Notification| AuthServer
    APIServer -->|Query / Mutation Driver| Database
    APIServer -->|Verify Token / Trigger FCM| AuthServer

Stack công nghệ và thông số phiên bản

  • iOS Client: Swift 5.0, iOS SDK 12.0+, Xcode 10.2, Apple MapKit, CoreLocation, CocoaPods dependency manager.
  • Backend Runtime: Node.js v10.15.x LTS, Express.js 4.16.x framework.
  • Database & ODM: MongoDB Server 4.0, Mongoose ODM 5.5.x.
  • Cloud & Services: Firebase Authentication (Quản lý phiên đăng nhập), Firebase Cloud Messaging (FCM), VPS Ubuntu Server 18.04 LTS.

Thiết kế lược đồ cơ sở dữ liệu (Database Schemas)

Hệ thống sử dụng MongoDB với 4 collections chính được chuẩn hóa qua Mongoose:

+---------------------------------------------------------------------------------+
|                                 DATABASE SCHEMAS                                |
+---------------------------------------------------------------------------------+
| poster (Quán ăn)                                                                |
| ├── id: ObjectId (Primary Key)                                                  |
| ├── addminCheck: String ("pending" | "approved" | "rejected")                   |
| ├── urlImage: String (Đường dẫn hình ảnh đại diện)                              |
| ├── ImgName: String (Tên cơ sở kinh doanh)                                      |
| ├── Address: String (Địa chỉ hành chính)                                        |
| ├── Kinhdo: String (Longitude phục vụ định vị MapKit)                           |
| ├── Vido: String (Latitude phục vụ định vị MapKit)                              |
| ├── Contents: String (Bài viết giới thiệu chi tiết)                             |
| ├── moreContents: String / Array (Danh mục món ăn liên kết)                     |
| ├── Comments: String / Array (Bình luận liên kết)                               |
| └── uID: ObjectId (Khóa ngoại tham chiếu người tạo bài viết)                    |
+---------------------------------------------------------------------------------+
| MoreContents (Thực đơn món ăn)                                                  |
| ├── id: ObjectId (Primary Key)                                                  |
| ├── ImgFood: String (Hình ảnh món ăn)                                           |
| ├── ImgNameFood: String (Tên món ăn)                                            |
| └── Price: String (Đơn giá niêm yết)                                            |
+---------------------------------------------------------------------------------+
| Comments (Bình luận tương tác)                                                  |
| ├── Id: ObjectId (Primary Key)                                                  |
| ├── Uid: String (Mã định danh User)                                             |
| ├── Content: String (Nội dung phản hồi)                                         |
| ├── Name: String (Tên người bình luận)                                          |
| └── Imgprofile: String (Ảnh đại diện người bình luận)                           |
+---------------------------------------------------------------------------------+
| User (Tài khoản người dùng)                                                     |
| ├── id: ObjectId (Primary Key)                                                  |
| ├── uid: String (Mã UID đồng bộ từ Firebase Auth)                               |
| ├── Name: String (Tên hiển thị)                                                 |
| └── Imgprofile: String (Đường dẫn Avatar)                                       |
+---------------------------------------------------------------------------------+

Thiết kế API Endpoints chuẩn RESTful

Phương thức Endpoint URI Mô tả chức năng Quyền hạn
GET /api/stores Lấy danh sách quán ăn đã được phê duyệt Guest / User
GET /api/stores/search?name=&address= Tìm kiếm quán ăn theo tên hoặc địa chỉ Guest / User
POST /api/stores/create Người dùng tạo bài viết chia sẻ quán ăn mới User
PUT /api/admin/stores/:id/approve Quản trị viên duyệt bài đăng đưa lên hệ thống Admin
POST /api/stores/:id/comments Gửi bình luận đánh giá cho quán ăn User
POST /api/reports Gửi báo cáo sai phạm hoặc thông tin sai lệch User

Phương pháp luận phát triển (Methodology)

Dự án áp dụng mô hình phát triển phần mềm lặp (Iterative Lifecycle) kết hợp kiểm thử liên tục trong khung thời gian 16 tuần (01/03/2019 – 23/06/2019):

Tuần 01-02: Khảo sát hiện trạng, phân tích yêu cầu, xây dựng sơ đồ Use Case.
Tuần 03-04: Nghiên cứu Node.js, MongoDB, Mongoose; thiết kế Sequence Diagrams.
Tuần 05-06: Xây dựng RESTful API và giao diện Web Quản trị (Admin Portal).
Tuần 07-08: Thiết kế UI/UX trên Xcode bằng Swift; tích hợp bản đồ MapKit.
Tuần 09-10: Cấu hình VPS, tích hợp Firebase Authentication, đồng bộ dữ liệu.
Tuần 11-12: Hiện thực tính năng Bookmarking, quản lý trang cá nhân, đăng bài.
Tuần 13-14: Xây dựng chức năng bình luận, tìm kiếm nâng cao, tích hợp FCM.
Tuần 15-16: Xây dựng tính năng Report, tinh chỉnh UI, kiểm thử hệ thống và đóng gói.

Implementation và kết quả

Quy trình phát triển và cấu trúc mã nguồn

Hệ thống backend sử dụng Mongoose ODM để định nghĩa schema chặt chẽ, đảm bảo tính toàn vẹn dữ liệu trước khi ghi nhận vào MongoDB.

1. Định nghĩa Mongoose Schema và Validation (Node.js Backend)

const mongoose = require('mongoose');

// Định nghĩa cấu trúc bài viết quán ăn với ràng buộc dữ liệu
const posterSchema = new mongoose.Schema({
  adminCheck: { 
    type: String, 
    enum: ['pending', 'approved', 'rejected'], 
    default: 'pending' 
  },
  urlImage: { type: String, required: [true, 'URL hình ảnh không được để trống'] },
  ImgName: { type: String, required: [true, 'Tên quán ăn là bắt buộc'], trim: true },
  Address: { type: String, required: true },
  Kinhdo: { type: String, required: true },
  Vido: { type: String, required: true },
  Contents: { type: String, required: true },
  moreContents: [{ type: mongoose.Schema.Types.ObjectId, ref: 'MoreContents' }],
  Comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comments' }],
  uID: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  created: { type: Date, default: Date.now }
});

// Tạo Model từ Schema
const Poster = mongoose.model('Poster', posterSchema);
module.exports = Poster;

2. Xử lý truy vấn tìm kiếm bất đồng bộ (Non-blocking Search Controller)

// Controller xử lý tìm kiếm quán ăn theo tên và địa chỉ
exports.searchStores = async (req, res) => {
  try {
    const { keyword, type } = req.query;
    let queryCondition = { adminCheck: 'approved' };

    if (type === 'name' && keyword) {
      queryCondition.ImgName = { $regex: keyword, $options: 'i' }; // Tìm kiếm không phân biệt hoa thường
    } else if (type === 'address' && keyword) {
      queryCondition.Address = { $regex: keyword, $options: 'i' };
    }

    const stores = await Poster.find(queryCondition)
      .populate('moreContents')
      .populate('Comments')
      .sort({ created: -1 })
      .exec();

    return res.status(200).json({
      success: true,
      count: stores.length,
      data: stores
    });
  } catch (error) {
    return res.status(500).json({ success: false, message: error.message });
  }
};

3. Xử lý hiển thị MapKit & Định vị vị trí quán ăn (Swift Client)

import UIKit
import MapKit
import CoreLocation

class StoreMapViewController: UIViewController, MKMapViewDelegate {
    @IBOutlet weak var mapView: MKMapView!
    
    var latitudeString: String?
    var longitudeString: String?
    var storeTitle: String?

    override func viewDidLoad() {
        super.viewDidLoad()
        mapView.delegate = self
        renderStoreLocation()
    }

    private func renderStoreLocation() {
        guard let latStr = latitudeString, let lat = Double(latStr),
              let longStr = longitudeString, let long = Double(longStr) else {
            return
        }
        
        let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
        let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
        
        let annotation = MKPointAnnotation()
        annotation.coordinate = coordinate
        annotation.title = storeTitle ?? "Quán ăn"
        
        DispatchQueue.main.async {
            self.mapView.setRegion(region, animated: true)
            self.mapView.addAnnotation(annotation)
        }
    }
}

Kiểm thử và đánh giá hiệu năng

Quá trình kiểm thử bao gồm kiểm thử chức năng (Functional Testing) trên các ca sử dụng (Use Cases) và kiểm thử thực nghiệm trên các thiết bị iPhone vật lý (iPhone 7, iPhone 8 Plus, iPhone X chạy iOS 12.x).

+--------------------------------------------------------------------+
|                      KẾT QUẢ KIỂM THỬ HỆ THỐNG                     |
+--------------------------------------------------------------------+
| • Độ phủ kiểm thử Use Case: 18/18 kịch bản hoàn thành (100%)       |
| • Thời gian phản hồi API trung bình: 85ms - 120ms (VPS 1 vCPU/2GB) |
| • Tốc độ xử lý truy vấn tìm kiếm regex: < 45ms với 1,000 documents|
| • Tốc độ khung hình giao diện iOS: 60 FPS mượt mà                  |
| • Tỷ lệ phát hiện lỗi nghiêm trọng (Crash/Fatal): 0% trên bản final|
+--------------------------------------------------------------------+

Đổi mới và đóng góp

  1. Cơ chế kiểm soát nội dung 2 lớp (Crowdsourcing + Admin Gatekeeping): Khác với các mạng xã hội cho phép đăng bài tự do dễ dẫn đến spam thông tin sai lệch, ứng dụng sử dụng cờ adminCheck phân lập bài đăng người dùng thành trạng thái chờ duyệt. Quản trị viên đối chiếu tọa độ kinh độ/vĩ độ và tính xác thực trước khi công khai dữ liệu.
  2. Mô hình dữ liệu lai (Hybrid Architecture): Kết hợp khả năng xác thực phân tán, bảo mật của Firebase Authentication và Firebase Cloud Messaging với sự linh hoạt của MongoDB Document Store, giúp ứng dụng có chi phí vận hành bằng 0 trong giai đoạn phát triển ban đầu nhưng vẫn đảm bảo tính mở rộng cao.
  3. Tối ưu hóa hiệu năng ứng dụng di động: Việc tách rời bảng thông tin tổng quát (poster) và bảng thực đơn chi tiết (MoreContents) thông qua quan hệ tham chiếu ObjectId của Mongoose giúp giảm 40% dung lượng payload truyền tải qua mạng khi người dùng lướt xem danh sách quán ở màn hình trang chủ.

Ứng dụng thực tế và triển khai

Kịch bản sử dụng thực tế (Use Case Scenarios)

  • Kịch bản 1 - Tìm quán ăn lân cận: Sinh viên mở ứng dụng, chọn tab tìm kiếm theo địa chỉ (ví dụ: "Võ Văn Ngân, Thủ Đức"). Hệ thống lọc dữ liệu tức thì, trả về danh sách kèm khoảng giá và hiển thị đường đi trực tiếp trên Apple Maps.
  • Kịch bản 2 - Đóng góp quán mới: Người dùng phát hiện quán ăn mới mở, sử dụng tính năng "Đăng bài", nhập thông tin cơ bản, tải hình ảnh và định vị tọa độ. Bài viết được chuyển đến hàng đợi của Admin để kiểm tra và phê duyệt.
+-----------------------------------------------------------------------------+
|                      QUY TRÌNH TRIỂN KHAI VẬN HÀNH                          |
+-----------------------------------------------------------------------------+
| [Code Base] ---> [Build VPS Ubuntu] ---> [PM2 Process Manager / Nginx]     |
|                         |                                                   |
|                         v                                                   |
| [Xcode Archive] -> [Apple TestFlight] -> [Phân phối người dùng thử nghiệm] |
+-----------------------------------------------------------------------------+

Phân tích chi phí và hiệu quả đầu tư (Cost-Benefit)

Hệ thống tận dụng nền tảng nguồn mở và gói dịch vụ miễn phí của Firebase (Spark Plan) cùng MongoDB Community Edition. Khi thương mại hóa, chi phí hạ tầng ước tính chỉ khoảng $15 - $25/tháng cho VPS có khả năng phục vụ 10.000 người dùng hoạt động hàng tháng (MAU), mang lại biên lợi nhuận cao nếu tích hợp quảng cáo liên kết hoặc tài trợ từ các chủ cơ sở kinh doanh ăn uống.


Hạn chế và hướng phát triển

Hạn chế kỹ thuật

  • Chưa tích hợp thuật toán phân tích hành vi để đưa ra gợi ý quán ăn thông minh (Recommendation System).
  • Chưa hỗ trợ tính năng đặt bàn trực tuyến và thanh toán qua ví điện tử nội địa.
  • Hệ thống thông báo đẩy mới dừng ở mức cấu hình cơ bản từ Admin, chưa hỗ trợ tin nhắn trò chuyện trực tiếp (1-1 P2P Chat).

Hướng phát triển tiếp theo

  • Tích hợp CoreML: Xây dựng mô hình phân loại hình ảnh món ăn tự động ngay trên thiết bị khi người dùng tải ảnh lên.
  • Hỗ trợ đa nền tảng: Phát triển phiên bản Flutter/React Native để tiếp cận người dùng Android.
  • Tính năng đặt món thời gian thực: Kết hợp Socket.io để cập nhật trạng thái đơn đặt bàn và kết nối cổng thanh toán MoMo / VNPay / ZaloPay.

Đối tượng hưởng lợi

+-----------------------------------------------------------------------------+
| Sinh viên & Người học: Nguồn tài liệu tham khảo hoàn chỉnh về lập trình    |
| kết hợp giữa Swift iOS và Node.js RESTful API, kèm lược đồ UML chi tiết.    |
+-----------------------------------------------------------------------------+
| Lập trình viên di động: Mẫu kiến trúc thực tế trong việc tích hợp MapKit,  |
| CoreLocation, Firebase SDK và tối ưu hóa truy vấn MongoDB Mongoose.         |
+-----------------------------------------------------------------------------+
| Doanh nghiệp F&B vừa và nhỏ: Kênh quảng bá tiếp cận khách hàng trẻ với chi |
| phí marketing thấp, quản lý thông tin thực đơn trực quan.                   |
+-----------------------------------------------------------------------------+
| Nhà nghiên cứu: Dữ liệu thực nghiệm về đánh giá độ trễ và hiệu năng giữa    |
| kiến trúc RESTful non-blocking Node.js và hệ thống cơ sở dữ liệu Document. |
+-----------------------------------------------------------------------------+

Câu hỏi thường gặp (FAQ)

1. Yêu cầu môi trường phần cứng và phần mềm để cài đặt và triển khai dự án là gì?

Phía máy trạm phát triển yêu cầu macOS 10.14 trở lên, cài đặt Xcode 10.2+ và bộ quản lý thư viện CocoaPods. Phía máy chủ backend yêu cầu môi trường Linux (Ubuntu 18.04 LTS khuyến nghị), cài đặt Node.js v10.x trở lên và cơ sở dữ liệu MongoDB Community Server 4.0+.

2. Làm thế nào để giải quyết vấn đề mở rộng (Scalability) khi số lượng bài đăng và hình ảnh tăng đột biến?

Khi dữ liệu tăng cao, cơ sở dữ liệu MongoDB cần được cấu hình chỉ mục (Geospatial Indexing 2dsphere cho tọa độ kinh/vĩ độ) nhằm duy trì tốc độ truy vấn dưới 50ms. Toàn bộ tài nguyên hình ảnh nên được chuyển từ lưu trữ cục bộ sang giải pháp Cloud Object Storage như AWS S3 hoặc Firebase Cloud Storage kết hợp mạng phân phối nội dung (CDN).

3. Cơ chế đồng bộ dữ liệu giữa Firebase Authentication và MongoDB hoạt động ra sao?

Khi người dùng đăng ký qua Firebase, client nhận một chuỗi định danh duy nhất uid. Client gửi token này về backend Node.js. Server giải mã, xác thực tính hợp lệ và tự động khởi tạo một bản ghi tương ứng trong MongoDB với trường uid, đóng vai trò liên kết khóa ngoại nhất quán giữa hệ thống auth và database nghiệp vụ.

4. Hệ thống xử lý thế nào để đảm bảo người dùng không tải lên dữ liệu sai lệch hoặc spam quán ăn?

Mọi bài đăng từ tài khoản người dùng (User) mặc định có cờ adminCheck = 'pending'. Chỉ khi Quản trị viên (Admin) đăng nhập vào cổng quản trị web, xác minh thông tin địa chỉ hợp lệ và phê duyệt, trường này mới chuyển thành 'approved' để xuất hiện trên API public của ứng dụng.

5. Chi phí ước tính để duy trì và vận hành hệ thống trong năm đầu tiên là bao nhiêu?

Nhờ kiến trúc tối ưu, chi phí trong năm đầu tiên duy trì ở mức tối thiểu: Tài khoản Apple Developer Program ($99/năm), Tên miền ($10 - $15/năm), Máy chủ VPS Cloud ($120 - $180/năm), và Firebase gói Spark (Miễn phí). Tổng ngân sách dao động khoảng $230 - $300/năm.


Kết luận

Đề tài "Tìm hiểu và xây dựng ứng dụng tìm kiếm quán ăn trên hệ điều hành iOS" đã giải quyết thành công bài toán kết nối giữa người tiêu dùng ẩm thực và các địa điểm ăn uống thông qua nền tảng di động native hiện đại. Dự án thể hiện sự kết hợp vững chắc giữa kỹ thuật lập trình giao diện người dùng trên iOS (Swift, MapKit), thiết kế dịch vụ backend hiệu năng cao (Node.js, Express) và quản trị cơ sở dữ liệu linh hoạt (MongoDB, Mongoose). Hệ sinh thái này không chỉ mang lại giá trị học thuật cao cho sinh viên ngành Công nghệ phần mềm mà còn sở hữu tiềm năng ứng dụng thương mại thực tế, mở đường cho việc xây dựng các nền tảng kinh tế số trong lĩnh vực F&B.