Skip to content

Estate Model Comparison: Legacy vs. New

Overview

This document compares the old (legacy) estate model from Vitec/Webmegler with the new estate model designed for Destinet. The new model is designed to work with Vitec data while providing a cleaner, more maintainable structure.


Key Problems with Legacy Model

1. System-Specific Field Names

The legacy model is tightly coupled to specific Norwegian real estate platforms:

// Legacy - Platform-specific fields
public string FinnEiendomstype { get; set; }
public string FinnOppdragstype { get; set; }
public string FinnEierformBygninger { get; set; }
public string TindeOppdragstype { get; set; }
public string TindeEiendomstype { get; set; }
public string KundenummerZett { get; set; }

Problems: - Not reusable with other systems - Duplicated data for each platform - Hard to maintain when platforms change - Cannot easily add new platforms

2. Mixed Concerns

Property data is mixed with presentation URLs and department information:

// Legacy - Everything in one flat class
public string Adresse { get; set; }                    // Property data
public string UrlNettversjon { get; set; }             // Presentation URL
public string AvdelingNavn { get; set; }               // Department data
public string AvdelingFagansvarligNavn { get; set; }   // Agent data
public bool Markedsforingsklart { get; set; }          // System state

Problems: - Difficult to understand what belongs where - Cannot easily update just property data - Hard to test individual components - Violates Single Responsibility Principle

3. Bloated Image Storage

Each image has 5 different URLs and 5 different filenames:

// Legacy - 10 properties per image!
public string UrlThumbnail { get; set; }
public string ThumbnailFil { get; set; }
public string UrlLiteBilde { get; set; }
public string LiteBildeFil { get; set; }
public string UrlStandardBilde { get; set; }
public string StandardBildeFil { get; set; }
public string UrlStorThumbnail { get; set; }
public string StorThumbnailFil { get; set; }
public string UrlOriginalBilde { get; set; }
public string OriginalBildeFil { get; set; }

Problems: - Extremely verbose - Difficult to work with programmatically - Wastes memory - Hard to add new image sizes

4. Norwegian Field Names

// Legacy - Norwegian names
public string Postnummer { get; set; }
public string Poststed { get; set; }
public string Byggeaar { get; set; }
public string Tomteareal { get; set; }
public decimal? Primaerrrom { get; set; }
public decimal? Bruksareal { get; set; }

Problems: - Not international - Harder for non-Norwegian developers - Inconsistent with modern C# naming conventions - Cannot be easily used in other countries

5. Unclear Data Types

// Legacy - Using strings for structured data
public string Oppvarming { get; set; }          // Should be enum or object
public string TypeOppdrag { get; set; }         // Should be enum
public string TypeEiendomstyper { get; set; }   // Should be enum

Problems: - No type safety - Easy to make typos - No IntelliSense help - No validation

6. Flat Structure

Everything is at the root level - over 100+ properties:

public class LegacyEneiendom
{
    // 120+ properties all at the same level
    public string Adresse { get; set; }
    public string Postnummer { get; set; }
    public string Poststed { get; set; }
    public string Kommune { get; set; }
    public string Fylke { get; set; }
    public string Bydel { get; set; }
    public string Land { get; set; }
    // ... 100+ more ...
}

Problems: - Overwhelming for developers - No logical grouping - Hard to find what you need - Difficult to validate


New Model Solutions

1. Clean Structure with Logical Grouping

// New - Organized into logical sections
public class Estate
{
    // Core Identification
    public string Heading { get; set; }
    public string EstateId { get; set; }
    public string AssignmentNum { get; set; }
    public int DepartmentId { get; set; }
    public DateTime ChangedDate { get; set; }

    // Brokers
    public List<BrokerWithRole> BrokersIdWithRoles { get; set; }

    // Classification (int enums from Vitec)
    public int EstateBaseType { get; set; }
    public int AssignmentTypeGroup { get; set; }
    public int Ownership { get; set; }

    // Status
    public int Status { get; set; }
    public DateTime? SoldDate { get; set; }
    public DateTime? ExpireDate { get; set; }

    // Location (grouped in separate classes)
    public Address Address { get; set; }
    public GeoCoordinates GeoCoordinates { get; set; }
    public List<Matrikkel> Matrikkel { get; set; }

    // Property Details (direct properties + complex objects)
    public int? NoOfRooms { get; set; }
    public int? NoOfBedRooms { get; set; }
    public int? NoOfBathRooms { get; set; }
    public int? Floor { get; set; }
    public int? ConstructionYear { get; set; }
    public Plot Plot { get; set; }
    public EstateSize EstateSize { get; set; }

    // Energy
    public EnergyRating EnergyRating { get; set; }

    // Pricing
    public EstatePrice EstatePrice { get; set; }

    // Ownership (for cooperatives)
    public PartOwnership PartOwnership { get; set; }

    // Facilities
    public List<string> EstateFacilities { get; set; }
    public List<int> Facilities { get; set; }

    // Showings
    public List<Showing> Showings { get; set; }
    public string ShowingNote { get; set; }

    // Media & Links (populated from separate endpoints)
    public ExternalPlatformUrls ExternalPlatformUrls { get; set; }
    public List<PropertyLink> Links { get; set; }
    public List<Image> Images { get; set; }
    public List<PropertyDocument> Documents { get; set; }

    // Description
    public List<DescriptionSection> DescriptionSections { get; set; }

    // Project Reference
    public string ProjectId { get; set; }

    // System Metadata
    public string Origin { get; set; }
    public Dictionary<string, object> CustomData { get; set; }
}

Benefits: - Clear sections with comments - Related properties grouped together - Easy to find what you need - More maintainable - Follows SOLID principles

2. Separation of Concerns

Instead of embedding department and employee data, the new model uses references:

// New - References via IDs (NOT embedded objects)
public int DepartmentId { get; set; }                     // → Department collection
public List<BrokerWithRole> BrokersIdWithRoles { get; set; }  // → Employee collection

// BrokerWithRole contains:
public class BrokerWithRole
{
    public string EmployeeId { get; set; }    // Reference to Employee
    public int BrokerRole { get; set; }       // Role enum (primary, secondary, etc.)
}

Benefits: - Department and Employee are separate master entities - Update an employee's email once → reflects on all estates automatically - No duplicated data across 150+ estates - Normalized architecture - Can test each component separately

3. Clean Image Model

// New - Simple, shared image class
public class Image
{
    public string Id { get; set; }
    public string OriginalUrl { get; set; }
    public string Filename { get; set; }
    public string FileExtension { get; set; }
    public string ExternalProviderUrl { get; set; }
    public string Caption { get; set; }
    public string AltText { get; set; }
    public string Category { get; set; }  // "Facade", "Interior", "Kitchen"
    public int Order { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    public DateTime LastModifiedOrigin { get; set; }
    public DateTime LastModifiedLocal { get; set; }
}

Benefits: - Clean, readable structure (12 properties vs. 10+ URLs per image in legacy) - Shared across all entities (Estate, Project, Department, Employee) - Flexible Category field adapts to context - Contains all necessary metadata in one place - Fetched from separate API endpoint and populated into list

4. International English Names

// New - International English
public string EstateId { get; set; }           // vs. legacy: Id
public string AssignmentNum { get; set; }      // vs. legacy: Oppdragsnummer
public int? NoOfRooms { get; set; }            // vs. legacy: AntallRom
public int? ConstructionYear { get; set; }     // vs. legacy: Byggeaar

// Address class
public string Street { get; set; }             // vs. legacy: Adresse
public string ZipCode { get; set; }            // vs. legacy: Postnummer
public string City { get; set; }               // vs. legacy: Poststed
public string Municipality { get; set; }       // vs. legacy: Kommune
public string County { get; set; }             // vs. legacy: Fylke

// EstateSize class
public decimal? PrimaryRoomArea { get; set; }  // vs. legacy: Primaerrrom
public decimal? UsableArea { get; set; }       // vs. legacy: Bruksareal
public decimal? PlotArea { get; set; }         // vs. legacy: Tomteareal

Benefits: - International standard - Easy for any developer to understand - Follows C# naming conventions - Consistent naming across all models

5. Proper Data Types with Enum Values

// New - Integer enums from source system (Vitec)
public int EstateBaseType { get; set; }      // 0=NotSet, 1=Detached, 2=Leisure, etc.
public int AssignmentTypeGroup { get; set; } // 0=NotSet, 1=Sale, 2=Rent, etc.
public int Ownership { get; set; }           // 0=Owned, 1=Cooperative, 2=Stock, etc.
public int Status { get; set; }              // 0=Request, 1=Preparation, 2=ForSale, etc.

// System metadata for platform-specific data
public string Origin { get; set; }           // "Vitec"
public Dictionary<string, object> CustomData { get; set; }

Benefits: - Preserves exact source data (Vitec enum values) - No conversion overhead - Type safety with integers - Can use enum mapping tables to convert to human-readable strings - System-specific data stored separately in CustomData

6. Organized Complex Objects

Instead of flat structure, related data is grouped:

// Address - All location data together
public class Address
{
    public string Street { get; set; }
    public string PublicApartmentNumber { get; set; }
    public string LocalAreaName { get; set; }
    public string ZipCode { get; set; }
    public string City { get; set; }
    public string Municipality { get; set; }
    public string County { get; set; }
    public string Country { get; set; }
    public string CountryCode { get; set; }
}

// EstateSize - All area measurements together
public class EstateSize
{
    public decimal? PrimaryRoomArea { get; set; }
    public decimal? GrossArea { get; set; }
    public decimal? UsableArea { get; set; }
    // ... other area measurements
}

// EstatePrice - All pricing information together
public class EstatePrice
{
    public decimal? PriceSuggestion { get; set; }
    public decimal? SoldPrice { get; set; }
    public decimal? CollectiveDebt { get; set; }
    public RentInfo Rent { get; set; }
    public decimal? PurchaseCostsAmount { get; set; }
    public decimal? TotalPrice { get; set; }
    // ... other price-related fields
}

// PartOwnership - Cooperative/condominium details
public class PartOwnership
{
    public string PartName { get; set; }
    public string PartOrgNumber { get; set; }
    public decimal? Deposit { get; set; }
    public decimal? ShareJointDebtYear { get; set; }
    // ... other cooperative fields
}

Benefits: - Related properties grouped together - Easy to find what you need - Can pass around logical units (e.g., just Address) - More maintainable and testable

7. Multi-Endpoint Data Population

The new model is designed to be populated from multiple API endpoints:

// Fetch from multiple endpoints and combine
var estate = FetchEstateBase(estateId);           // Base data
estate.Images = FetchEstateImages(estateId);       // Separate endpoint
estate.Documents = FetchEstateDocuments(estateId); // Separate endpoint
estate.Links = FetchEstateLinks(estateId);         // Separate endpoint
estate.Showings = FetchEstateShowings(estateId);   // Separate endpoint

Benefits: - Clean separation of data sources - Estate object serves as complete, denormalized view once populated - Can fetch only what you need - Matches Vitec API architecture


Side-by-Side Comparison

Address Information

Legacy New
7 flat properties 1 Address object
Adresse, Postnummer, Poststed, Kommune, Fylke, Bydel, Land Street, ZipCode, City, Municipality, County, LocalAreaName, Country
Norwegian names English names
No structure Clean Address class
Mixed with other data Separate shared class

Images

Legacy New
10 properties per image 1 Image object with 12 properties
5 URLs + 5 filenames per size Single OriginalUrl + metadata
Confusing naming Clear property names
Hard to iterate Easy List<Image>
Estate-specific Shared across all entities
Embedded in estate data Fetched from separate endpoint

Property Types

Legacy New
Multiple string fields per platform Integer enum values
FinnEiendomstype, TindeEiendomstype, TypeEiendomstyper EstateBaseType = 1 (Vitec enum)
Platform-specific strings Source system enum values
Duplicated data Single source of truth
No validation Type-safe integers

Department/Employee Info

Legacy New
30+ prefixed flat properties embedded References via IDs
AvdelingNavn, AvdelingFagansvarligNavn, etc. DepartmentId, BrokersIdWithRoles
Duplicated on every estate Stored once, referenced many times
Mixed with property data Normalized architecture
Update employee email = update 150+ estates Update employee email = update 1 employee record

Pricing

Legacy New
20+ flat price properties 1 EstatePrice object
Mixed throughout the class All pricing data grouped
Prisantydning, Fellesgjeld, Omkostninger, etc. PriceSuggestion, CollectiveDebt, PurchaseCostsAmount
Norwegian names English names
No structure EstatePrice class with RentInfo subobject

Area Measurements

Legacy New
10+ flat area properties 1 EstateSize object
Primaerrrom, Bruksareal, BRA, BRAE, etc. PrimaryRoomArea, UsableArea, GrossArea, etc.
Norwegian abbreviations Clear English names
Scattered in class Grouped in EstateSize class

Migration Path

Step 1: Map Legacy to New

Create a mapper that converts LegacyEneiendomEstate:

public class VitecEstateMapper
{
    public Estate MapToEstate(LegacyEneiendom legacy)
    {
        return new Estate
        {
            EstateId = legacy.Id.ToString(),
            AssignmentNum = legacy.Oppdragsnummer.ToString(),
            Heading = legacy.Tittel,
            DepartmentId = legacy.AvdelingId,
            ChangedDate = legacy.EndretDato,

            // Classification - convert from strings to Vitec enum values
            EstateBaseType = MapEstateBaseType(legacy.TypeEiendomstyper),
            Ownership = MapOwnership(legacy.Eierform),
            Status = MapStatus(legacy.Status),

            // Location
            Address = new Address
            {
                Street = legacy.Adresse,
                ZipCode = legacy.Postnummer,
                City = legacy.Poststed,
                Municipality = legacy.Kommune,
                County = legacy.Fylke,
                Country = legacy.Land
            },

            GeoCoordinates = new GeoCoordinates
            {
                Latitude = legacy.Latitude,
                Longitude = legacy.Longitude
            },

            // Property Details
            NoOfRooms = legacy.AntallRom,
            NoOfBedRooms = legacy.AntallSoverom,
            ConstructionYear = ParseYear(legacy.Byggeaar),

            // Area measurements
            EstateSize = new EstateSize
            {
                PrimaryRoomArea = legacy.Primaerrrom,
                UsableArea = legacy.Bruksareal,
                GrossArea = legacy.BRA
            },

            // Pricing
            EstatePrice = new EstatePrice
            {
                PriceSuggestion = legacy.Prisantydning,
                CollectiveDebt = legacy.Fellesgjeld
            },

            // Origin tracking
            Origin = "Vitec",

            // System metadata for truly Vitec-specific data
            CustomData = new Dictionary<string, object>
            {
                ["finn_orderno"] = legacy.FinnOrderno,
                ["webmegler_database_id"] = legacy.Databasenummer
            }
        };
    }

    private int MapEstateBaseType(string legacyType)
    {
        // Convert legacy string to Vitec enum value
        return legacyType switch
        {
            "Bolig" => 1,      // Detached
            "Fritid" => 2,     // Leisure
            "Næring" => 3,     // Business
            _ => 0             // NotSet
        };
    }
}

Step 2: Handle Images Separately

// Legacy had images embedded
// New model fetches from separate endpoint
estate.Images = await FetchImagesFromVitecApi(estateId);

Step 3: Map Brokers/Employees

// Legacy had embedded agent data
// New model uses references
estate.BrokersIdWithRoles = new List<BrokerWithRole>
{
    new BrokerWithRole
    {
        EmployeeId = legacy.FagansvarligId.ToString(),
        BrokerRole = 1  // Primary
    }
};

Step 4: Gradually Migrate

  • Keep both models during transition
  • Use new model for new features
  • Migrate existing features incrementally
  • Remove legacy model when fully migrated

Benefits Summary

The new Estate model provides:

Clean structure - organized, logical grouping with sections ✅ Separation of concerns - references instead of embedded data ✅ International - English naming, usable globally ✅ Type safety - integer enums from source system ✅ Maintainability - easy to understand and extend ✅ Best practices - follows SOLID principles ✅ Shared classes - Image, Address, etc. reused across all models ✅ Multi-endpoint support - designed for modern API patterns ✅ System metadata - flexible storage for platform-specific data

When to Use Each Model

Legacy Model: - Documentation purposes only - Migration reference - Understanding the source XML structure - DO NOT USE for new development

New Estate Model: - All new development - Working with Vitec data - Building new features - Creating APIs and services

For all new development, use the new Estate model.