Skip to content

Latest commit

Β 

History

54 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌟 ARSample - Augmented Reality Object Placement

Kotlin Compose Multiplatform License Platform

A professional Kotlin Multiplatform AR application demonstrating Clean Architecture, DDD principles, and platform-specific AR implementations (ARCore/ARKit)

Features β€’ Architecture β€’ Tech Stack β€’ Getting Started β€’ Documentation


πŸ“± Overview

ARSample is a production-ready Augmented Reality application built with Kotlin Multiplatform Mobile (KMM), showcasing enterprise-grade architecture patterns and modern mobile development practices. The app allows users to import, place, and manage 3D objects in AR scenes across both Android and iOS platforms with a fully shared business logic layer.

🎯 Key Highlights

  • πŸ—οΈ Clean Architecture + DDD: Domain-driven design with clear separation of concerns
  • πŸ”„ 95%+ Code Sharing: Business logic, UI, and domain layer shared between platforms
  • πŸ“ MVVM Pattern: Reactive state management with Kotlin Flow
  • 🎨 Jetpack Compose: Modern declarative UI for both platforms
  • πŸ§ͺ High Test Coverage: 85-100% test coverage with comprehensive unit tests
  • πŸ” Type Safety: Value Objects pattern for domain validation
  • πŸ“¦ Repository Pattern: Clean data abstraction with DTO/Mapper pattern

✨ Features

Core Functionality

  • βœ… 3D Model Import: Support for GLB and USDZ formats
  • βœ… AR Object Placement: Real-time hit testing and object positioning
  • βœ… Scene Persistence: Auto-save/restore AR scenes across app restarts
  • βœ… Object Management: Add, remove, and list imported 3D models
  • βœ… Cross-Platform UI: Identical user experience on Android and iOS

Technical Features

  • πŸ” Domain Validation: Value Objects with sealed classes (ModelUri, ObjectName)
  • πŸš€ Result Pattern: Type-safe error handling throughout the application
  • 🎯 Use Case Pattern: Single-responsibility business logic units
  • πŸ—‚οΈ Local Storage: Platform-specific implementations (DataStore/UserDefaults)
  • 🧩 Expect/Actual Pattern: Clean platform-specific abstractions

πŸ›οΈ Architecture

This project follows Eric Evans' Domain-Driven Design (DDD) + Clean Architecture principles with four distinct layers:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Presentation Layer                        β”‚
β”‚  β€’ ViewModels (State Management)                            β”‚
β”‚  β€’ Compose UI Screens                                       β”‚
β”‚  β€’ Platform-specific AR Views (AndroidView/UIViewWrapper)  β”‚
β”‚  β€’ Depends on: Application Layer                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Application Layer                          β”‚
β”‚  β€’ Use Cases (ImportObject, PlaceObject, RemoveObject)      β”‚
β”‚  β€’ Business Workflows                                       β”‚
β”‚  β€’ Use Case DTOs (Input/Output models)                      β”‚
β”‚  β€’ Depends on: Domain Layer only                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Domain Layer                             β”‚
β”‚  β€’ Entities (ARObject, ARScene, PlacedObject)               β”‚
β”‚  β€’ Value Objects (ModelUri, ObjectName)                     β”‚
β”‚  β€’ Repository Interfaces                                    β”‚
β”‚  β€’ Domain Exceptions                                        β”‚
β”‚  β€’ NO dependencies (innermost layer)                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β–²
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 Infrastructure Layer                         β”‚
β”‚  β€’ Repository Implementations                                β”‚
β”‚  β€’ DTOs + Mappers (Persistence)                             β”‚
β”‚  β€’ Local Data Sources (Platform-specific)                   β”‚
β”‚  β€’ File Storage (Internal Storage / Documents Directory)    β”‚
β”‚  β€’ Depends on: Domain Layer                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Layer Dependencies (DDD Structure)

Presentation β†’ Application β†’ Domain ← Infrastructure

Key Architectural Rules:

  1. Domain Layer (innermost): Pure business logic, zero dependencies
  2. Application Layer: Orchestrates domain objects, depends on Domain only
  3. Infrastructure Layer: Technical implementations, depends on Domain (not Application)
  4. Presentation Layer: UI layer, depends on Application

Architectural Decisions

1. Value Objects for Domain Validation

sealed class ModelUri private constructor(val value: String) {
    companion object {
        fun create(uri: String): Result<ModelUri> {
            return when {
                uri.isBlank() -> Result.failure(ValidationException("URI cannot be blank"))
                !uri.matches(Regex(".*\\.(glb|usdz)$")) -> 
                    Result.failure(ValidationException("Invalid model format"))
                else -> Result.success(ValidModelUri(uri))
            }
        }
    }
    private class ValidModelUri(value: String) : ModelUri(value)
}

2. Use Case Pattern

// Use cases in APPLICATION LAYER (not domain)
// Location: application/usecase/
interface ImportObjectUseCaseInterface : BaseUseCase<ImportObjectInput, ARObject>

class ImportObjectUseCase(
    private val repository: ARObjectRepository
) : ImportObjectUseCaseInterface {
    override suspend fun invoke(input: ImportObjectInput): Result<ARObject> {
        // Validation with Value Objects (from domain)
        val nameResult = ObjectName.create(input.name)
        if (nameResult.isFailure) return Result.failure(nameResult.exceptionOrNull()!!)
        
        return repository.importObject(input.uri, input.name, input.modelType)
    }
}

// Import paths:
import com.trendhive.arsample.application.usecase.ImportObjectUseCase
import com.trendhive.arsample.application.base.BaseUseCase
import com.trendhive.arsample.application.dto.ImportObjectInput

3. DTO + Mapper Pattern

// Persistence DTO in INFRASTRUCTURE LAYER
// Location: infrastructure/persistence/dto/
@Serializable
data class ARObjectDTO(
    val id: String,
    val name: String,
    val modelUri: String,
    val modelType: String
)

// Mapper in INFRASTRUCTURE LAYER
// Location: infrastructure/persistence/mapper/
class ARObjectMapper : BaseMapper<ARObjectDTO, ARObject> {
    override fun toDTO(model: ARObject): ARObjectDTO
    override fun toModel(dto: ARObjectDTO): ARObject
}

// Import paths:
import com.trendhive.arsample.infrastructure.persistence.dto.ARObjectDTO
import com.trendhive.arsample.infrastructure.persistence.mapper.ARObjectMapper
import com.trendhive.arsample.infrastructure.persistence.BaseMapper

πŸ› οΈ Tech Stack

Core Technologies

AR Frameworks

Data & Storage

Testing

Build & Tooling


πŸš€ Getting Started

Prerequisites

Required:

  • JDK 17 or higher
  • Android Studio Ladybug (2024.2.1) or newer
  • Xcode 15+ (for iOS development)
  • macOS (for iOS builds)

AR Device Requirements:

  • Android: ARCore-supported device (Check compatibility)
  • iOS: A12+ chip with ARKit support (iPhone XS and newer)

Installation

  1. Clone the repository

    git clone https://github.com/recepteksi/ARSample.git
    cd ARSample
  2. Build Android

    ./gradlew :composeApp:assembleDebug
  3. Build iOS

    # Open in Xcode
    open iosApp/iosApp.xcodeproj
    # Or use xcodebuild
    xcodebuild -project iosApp/iosApp.xcodeproj -scheme iosApp -configuration Debug

Running Tests

# Run all tests
./gradlew :composeApp:testDebugUnitTest

# Run specific test class
./gradlew :composeApp:testDebugUnitTest --tests "com.trendhive.arsample.domain.usecase.ImportObjectUseCaseTest"

# Run with coverage
./gradlew :composeApp:testDebugUnitTest --tests "*" --info

πŸ“‚ Project Structure

ARSample/
β”œβ”€β”€ composeApp/src/
β”‚   β”œβ”€β”€ commonMain/kotlin/com/trendhive/arsample/
β”‚   β”‚   β”œβ”€β”€ domain/                   # Domain Layer (NO dependencies)
β”‚   β”‚   β”‚   β”œβ”€β”€ base/                 # BaseModel, BaseRepository
β”‚   β”‚   β”‚   β”œβ”€β”€ model/                # Domain entities (ARObject, ARScene, PlacedObject)
β”‚   β”‚   β”‚   β”‚   └── valueobjects/     # Value Objects (ModelUri, ObjectName)
β”‚   β”‚   β”‚   β”œβ”€β”€ repository/           # Repository interfaces
β”‚   β”‚   β”‚   └── exception/            # Domain exceptions
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ application/              # Application Layer (depends on Domain)
β”‚   β”‚   β”‚   β”œβ”€β”€ base/                 # BaseUseCase<Input, Output>
β”‚   β”‚   β”‚   β”œβ”€β”€ dto/                  # Use Case Input/Output DTOs
β”‚   β”‚   β”‚   └── usecase/              # Business workflows (use cases)
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ infrastructure/           # Infrastructure Layer (depends on Domain)
β”‚   β”‚   β”‚   └── persistence/
β”‚   β”‚   β”‚       β”œβ”€β”€ dto/              # Persistence DTOs
β”‚   β”‚   β”‚       β”œβ”€β”€ mapper/           # DTO ↔ Model mappers
β”‚   β”‚   β”‚       β”œβ”€β”€ repository/       # Repository implementations
β”‚   β”‚   β”‚       β”œβ”€β”€ local/            # Data source interfaces
β”‚   β”‚   β”‚       └── BaseMapper.kt     # Mapper base class
β”‚   β”‚   β”‚
β”‚   β”‚   └── presentation/             # Presentation Layer (depends on Application)
β”‚   β”‚       β”œβ”€β”€ viewmodel/            # State management
β”‚   β”‚       └── ui/                   # Compose screens and components
β”‚   β”‚
β”‚   β”œβ”€β”€ androidMain/                  # Android-specific (ARCore, DataStore)
β”‚   β”‚   β”œβ”€β”€ ar/                       # ARCore implementation
β”‚   β”‚   └── infrastructure/persistence/local/  # Android data sources
β”‚   β”‚
β”‚   β”œβ”€β”€ iosMain/                      # iOS-specific (ARKit, UserDefaults)
β”‚   β”‚   β”œβ”€β”€ ar/                       # ARKit implementation
β”‚   β”‚   └── infrastructure/persistence/local/  # iOS data sources
β”‚   β”‚
β”‚   └── commonTest/                   # Shared unit tests
β”‚
β”œβ”€β”€ iosApp/                           # iOS app entry point
β”œβ”€β”€ docs/                             # Architecture docs and guides
└── .claude/agents/                   # AI agent system documentation

πŸ§ͺ Testing Strategy

Test Coverage

  • Domain Layer: 90%+ coverage
  • Use Cases: 100% coverage
  • ViewModels: 85%+ coverage
  • Repositories: 90%+ coverage

Test Structure

class ImportObjectUseCaseTest {
    private lateinit var repository: ARObjectRepository
    private lateinit var useCase: ImportObjectUseCase
    
    @Test
    fun `import valid object succeeds`() = runTest {
        // Arrange
        val input = ImportObjectInput("file://model.glb", "Chair", ModelType.GLB)
        coEvery { repository.importObject(any(), any(), any()) } returns 
            Result.success(mockARObject)
        
        // Act
        val result = useCase(input)
        
        // Assert
        assertTrue(result.isSuccess)
        coVerify { repository.importObject("file://model.glb", "Chair", ModelType.GLB) }
    }
}

πŸ“š Documentation

Core Concepts

Platform-Specific


🎨 Key Design Patterns

1. Repository Pattern

interface ARObjectRepository : BaseRepository {
    suspend fun importObject(uri: String, name: String, type: ModelType): Result<ARObject>
    suspend fun getAllObjects(): Result<List<ARObject>>
    suspend fun deleteObject(id: String): Result<Unit>
}

2. Base Abstractions

// BaseModel and BaseRepository in DOMAIN layer
interface BaseModel
interface BaseRepository

// BaseUseCase in APPLICATION layer
interface BaseUseCase<Input : BaseModel, Output : BaseModel> {
    suspend operator fun invoke(input: Input): Result<Output>
}

// BaseMapper in INFRASTRUCTURE layer
interface BaseMapper<DTO, Model> {
    fun toDTO(model: Model): DTO
    fun toModel(dto: DTO): Model
}

// Import paths:
import com.trendhive.arsample.domain.base.BaseModel
import com.trendhive.arsample.domain.base.BaseRepository
import com.trendhive.arsample.application.base.BaseUseCase
import com.trendhive.arsample.infrastructure.persistence.BaseMapper

3. Exception Hierarchy

sealed class DomainException(message: String) : Exception(message)
class ValidationException(message: String) : DomainException(message)
class EntityNotFoundException(message: String) : DomainException(message)
class StorageException(message: String) : DomainException(message)

🀝 Contributing

Contributions are welcome! This project follows professional development practices:

  1. Code Standards: Kotlin conventions, Clean Architecture compliance
  2. Testing: All new features must include unit tests
  3. Documentation: Update relevant docs with changes
  4. Review Process: Code review checklist validation

See CONTRIBUTING.md for detailed guidelines.


πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ‘¨β€πŸ’» Author

Recep Tekşi


🌟 Showcase

This project demonstrates:

βœ… Modern Android/iOS Development - KMM, Compose, ARCore/ARKit
βœ… Enterprise Architecture - Clean Architecture, DDD, SOLID principles
βœ… Professional Practices - High test coverage, type safety, documentation
βœ… Platform Expertise - Native AR implementations, platform-specific optimizations
βœ… Team Collaboration - Multi-agent system, code review standards


Built with ❀️ using Kotlin Multiplatform

⭐ Star this repo if you find it useful!

About

Kotlin Multiplatform AR app with ARCore/ARKit - Clean Architecture + DDD

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages