public class ShipManager
{
	private ShipPersistenceService persistenceService;
	private DistanceCalculator distanceCalculator;
	Dictionary<string, ShipPositionUpdate> lastUpdates;

	public ShipManager()
	{
		persistenceService = new ShipPersistenceService();
		distanceCalculator = new DistanceCalculator();
		lastUpdates = new Dictionary<string, ShipPositionUpdate>();
	}

	public void PositionReceived(ShipPositionUpdate update)
	{
		var lastPosition = update.Position;
		if (lastUpdates.TryGetValue(update.ID, out ShipPositionUpdate last))
			lastPosition = last.Position;
		
		//how much has it moved?
		update.DistanceMovedMeters = distanceCalculator.DistanceInMeters(update.Position, lastPosition);
		update.ExpiresAtUTC = DateTime.UtcNow.AddHours(3);
		
		//store the update somewhere
		persistenceService.Persist(update);
		
		lastUpdates[update.ID] = update;
	}
}

public class ShipPositionUpdate
{
	public string ID { get; set; }
	public string Name { get; set; }
	public (decimal lat, decimal lon) Position { get; set; }
	public decimal? DistanceMovedMeters { get; set; }
	public DateTime ExpiresAtUTC { get; set; }
}

public class ShipPersistenceService
{
	//This method will go to the database
	public bool Persist(ShipPositionUpdate update) => true;
}

public class DistanceCalculator
{
	public decimal DistanceInMeters((decimal lat, decimal lon) p1, (decimal lat, decimal lon) p2) => 0; //irrelevant
}