Из использованных паттернов могу отметить рекурсивный вызов метода обработки URL через асинхронные делегаты (т.е. через пул потоков) - это распараллеливает загрузку и обработку страниц, а также избавляет от дамоклова меча переполнения стека. В папке солюшна есть диаграмма последовательности LinkLogger.LogLinks, описывающая алгоритм работы. В реальной жизни следовало бы, конечно, реализовать асинхронный вариант метода LogLinks() с возможностью отмены, но... этого не было в задании.