Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info

액터모델의 메일박스로 통해 전달되는 이벤트들은 다양한 DB장치를 이용해 영속화가 가능하며 상태프로그래밍을 할수 있습니다.

RDB,MongoDB,ElasticSearch등 다양한 특징을 가진 전통강호 영속장치를 이용해 각각 특화된 기능을  이용할수 있겠지만

이러한 기능을 보편적으로 모두이용할수 있는 장치가 없을까 알아보다 발견한 DB로 Akka를 연구하는 NetCorelabs에 변종실험 대상으로 추가하게되었습니다.

Image Modified

데이터베이스는 지루하면 안된다라는 슬로건을 내건 모던 DB

...

기존 시스템RavenDB로 대체 가능 여부설명
MongoDB (Document DB)✅ 완전 대체JSON 기반 문서 저장, 컬렉션 → 문서 분리 모델
Elasticsearch✅ 부분 대체Full-text 검색 지원, 복잡한 분석쿼리는 제한적이나 일반 검색에는 충분
Neo4j (Graph DB)⚠️ 간단한 관계 트래버설은 가능명시적 Graph 모델링은 어려움 (복잡한 네트워크 분석에는 부적합)
Vector DB (예: Weaviate, Milvus)✅ 단순 벡터 검색은 대체 가능다차원 벡터 검색 API 제공, 모델링+쿼리 결합 쉬움
RDB (CRUD/정형)⚠️ 단순 CRUD는 가능, 복잡한 조인과 트랜잭션은 제한적정형 테이블 기반보다는 문서 중심 모델 필요



RabenDB Docker StandAlone 구동

Code Block
themeEmacs
version: '3.8'

services:
  ravendb:
    image: ravendb/ravendb:ubuntu-latest
    container_name: ravendb
    ports:
      - "9000:8080"
    environment:
      - RAVEN_Setup_Mode=None
      - RAVEN_License_Eula_Accepted=true
    volumes:
      - ravendb_data:/ravendb/data
      - ravendb_logs:/ravendb/logs

volumes:
  ravendb_data:
  ravendb_logs:


  • 클라우드로도 이용가능하며~ 로컬또는 온프레미스로도 운영가능합니다. 


IDE 환경

Image Modified

  • Docker와 통합된 IDE환경으로 RavenDB 구동


RavenDB 웹관리툴

Image Modified

  • DB를 관리할 웹툴을 포함 기본제공하기 때문에 포함하며 기본적인 관리가 가능합니다.
  • IDE내에서 연동돠는 플러그인은 아직 없어보입니다.


언어별 지원 클라이언트

Image Modified

  • JAVA또는 Node.js가 일반적으로 첫번째 지원인경우가 많은데(노출순) .NET이 처음인것으로보아 다소 .NET친화적인 DB인듯.....

RabenClient for .NET

...

  • Akka.net을  연계해 사용예정이기때문에 .NET 기반실험이 진행되었습니다.


RabenClient for .NET

Image Added

  • 설치된 서버 Mazor버전과 맞춰서 패키지 설치


Database생성

Image Added


몽고DB와 유사하게 스키마리스 DB이기때문에 DDL코드가 필요로 하지 않습니다. 


Repository 코드

Code Block
themeEmacs
public class Member
{
    public string Id { get; set; } // RavenDB는 기본적으로 Id를 문서 키로 사용
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

public class MemberRepository
{
    private readonly IDocumentStore _store;

    public MemberRepository(IDocumentStore store)
    {
        _store = store ?? throw new ArgumentNullException(nameof(store));
    }

    public void AddMember(Member member)
    {
        using (var session = _store.OpenSession())
        {
            session.Store(member);
            session.SaveChanges();
        }
    }

    public Member GetMemberById(string id)
    {
        using (var session = _store.OpenSession())
        {
            return session.Load<Member>(id);
        }
    }

    public void UpdateMember(Member member)
    {
        using (var session = _store.OpenSession())
        {
            var existingMember = session.Load<Member>(member.Id);
            if (existingMember != null)
            {
                existingMember.Name = member.Name;
                existingMember.Email = member.Email;
                existingMember.Age = member.Age;
                session.SaveChanges();
            }
        }
    }

    public void DeleteMember(string id)
    {
        using (var session = _store.OpenSession())
        {
            var member = session.Load<Member>(id);
            if (member != null)
            {
                session.Delete(member);
                session.SaveChanges();
            }
        }
    }
}


CRUS TEST 코드

Code Block
themeEmacs
public class MemberRepositoryTest : TestKitXunit
{
    private readonly IDocumentStore _store;
    private readonly MemberRepository _repository;
    
    public MemberRepositoryTest(ITestOutputHelper output) : base(output)
    {
        // RavenDB 임베디드 서버 초기화
        _store = new DocumentStore
        {
            Urls = new[] { "http://localhost:9000" }, // 로컬 RavenDB URL
            Database = "net-core-labs"
        };
        _store.Initialize();

        // MemberRepository 초기화
        _repository = new MemberRepository(_store);
    }
    
    [Fact]
    public void AddMember_ShouldAddMemberSuccessfully()
    {
        // Arrange
        var member = new Member
        {
            Name = "John Doe",
            Email = "john.doe@example.com",
            Age = 30
        };

        // Act
        _repository.AddMember(member);

        // Assert
        var retrievedMember = _repository.GetMemberById(member.Id);
        Assert.NotNull(retrievedMember);
        Assert.Equal("John Doe", retrievedMember.Name);
    }

    [Fact]
    public void UpdateMember_ShouldUpdateMemberSuccessfully()
    {
        // Arrange
        var member = new Member
        {
            Name = "Jane Doe",
            Email = "jane.doe@example.com",
            Age = 25
        };
        _repository.AddMember(member);

        // Act
        member.Age = 26;
        _repository.UpdateMember(member);

        // Assert
        var updatedMember = _repository.GetMemberById(member.Id);
        Assert.NotNull(updatedMember);
        Assert.Equal(26, updatedMember.Age);
    }

    [Fact]
    public void DeleteMember_ShouldDeleteMemberSuccessfully()
    {
        // Arrange
        var member = new Member
        {
            Name = "Mark Smith",
            Email = "mark.smith@example.com",
            Age = 40
        };
        _repository.AddMember(member);

        // Act
        _repository.DeleteMember(member.Id);

        // Assert
        var deletedMember = _repository.GetMemberById(member.Id);
        Assert.Null(deletedMember);
    }
    
}
  • 처음 사용하는 DB사용 자체를 연구하고 테스트하는 코드이기때문에 Mocking을 이용하지 않고 로컬구축 DB직접 Access방식이용



테스트 수행및 확인

Image Added

Image Added

  • 비교적 심플한 코드작성으로 CRUD 테스트 수행이 완료되었습니다.